Powershell special characters

When you are using Powershell, it really helps if you use special characters to write your own scripts or interpret someone else’s scripts. Some of them are really common and get used very often….

# Hash – Single line comment


#This script is for ....
#This variable is to ...


$ Dollar sign – is used to declare a variable

$ComputerName = "Server1"
$password = "p@sw0rd"


| Pipeline- Executes the left side and with  the output feeds to the right


Get-Process | Select -first 10


% Percentage – Short for “FOREACH”


% ($Server in $Servers) { Write-Host $_}


? Question Mark – Short for “Where”


Get-process | ? {$_.name -like 'win*' -and $_.status -eq 'Running'}


@ () – Declares an array


$Servers = @ ("server1", "server2", "server3")


@ {} – Declares an hash table


$servers = @{"server1" = "Dell";
             "server2" = "HP";
             "server3" = "Nutanix"}


------------------------------------ OR

$params = @{};
$params['class'] = 'Win32_DiskDrive';
$params['filter'] = 'size=256052966400'; #find a drive which is 256GB in size
Get-WmiObject @params
Get-WmiObject -Class 'Win32_DiskDrive' -Filter 'size=256052966400'


& Ampersand – Executes strings as commandlets


& "Get-Scheduledjob"


! Exclamation – Short for “not”

$serverName = $null;
if(!$serverName) { Write-Host '$a is null' }


:: Double colon – Reference static member of a class. The class name must be enclosed in square brackets.

[string]::Equals = ("Computers", "COMPUTERS")
False # this will compare these two strings and returns false




PowerShell Script Monitors Security Logs and Sends Email Alerts

function Get-ADAuditLogsv2{

Param ($from = “abc@domain.local”,
$servers = (“DCVM01”),
$eventids = @(1076,1039),
$date = ((Get-Date).AddMinutes(-60))

$ErrorActionPreference= ‘silentlycontinue’
foreach ($server in $servers){
foreach ($eventid in $eventids) {

$events = Get-WinEvent -FilterHashtable @{logname=’security’;id=$eventid;StartTime=$date} -ComputerName $server
if ($events -ne $null){
foreach ($event in $events){
$eventsubject=$eventsubject.replace(“`n”, “”)
$eventsubject=$eventsubject.replace(“`r”, “”)
$body = @($timecreated,$eventmessage )| Out-String
$subject= “Event ID” + ” ” + $eventid + ” ” + $eventsubject
Send-MailMessage -Body $body -From $from -SmtpServer $smtpserver -Subject $subject -To $to


Get-Date | Out-File c:\errorlog.txt -Append -Force
$Error | Out-File c:\errorlog.txt -Append -Force

Monitoring Cluster Shared Volume – PowerShell

I have found this but haven't tested yet. I will tweak and use it soon.
#Cluster Shared Volume Free Disk Space
#Emails results of CSV free space on CLUSTER1
#Created 03-08-2011

#Import Failover Cluster PowerShell Module--------------------------------------------------------
Import-Module FailoverClusters

#Begin customization-------------------------
$SmtpServer = "mail.company.com" #Enter FQDN of SMTP server
$SmtpFrom = "CSV Status <CSVFreeSpace@company.com>" #Enter sender email address
$SmtpTo = "you@company.com" #Enter one or more recipient addresses in an array ("abc@company.com","def@company.com")
$SmtpSubject = "CLUSTER1 CSV Free Disk Space Report" #Enter subject of message
#End customization---------------------------

#Get Cluster Shared Volume details and put into array. Convert results from bytes into gigabytes.
$objs = @()

$csvs = Get-ClusterSharedVolume
foreach ( $csv in $csvs )
   $csvinfos = $csv | select -Property Name -ExpandProperty SharedVolumeInfo
   foreach ( $csvinfo in $csvinfos )
      $obj = New-Object PSObject -Property @{
         Name        = $csv.Name
         Path        = $csvinfo.FriendlyVolumeName
         Size        = $csvinfo.Partition.Size
         FreeSpace   = $csvinfo.Partition.FreeSpace
         UsedSpace   = $csvinfo.Partition.UsedSpace
         PercentFree = $csvinfo.Partition.PercentFree
      $objs += $obj

#Original code
#$objs | ft -auto Name,Path,@{ Label = "Size(GB)" ; Expression = { "{0:N2}" -f ($_.Size/1024/1024/1024) } },@{ Label = "FreeSpace(GB)" ; Expression = { "{0:N2}" -f ($_.FreeSpace/1024/1024/1024) } },@{ Label = "UsedSpace(GB)" ; Expression = { "{0:N2}" -f ($_.UsedSpace/1024/1024/1024) } },@{ Label = "PercentFree" ; Expression = { "{0:N2}" -f ($_.PercentFree) } }

#Give a brief description of the output
$output = "The following shows the amount of free space available on the cluster shared volumes on DRSRVVSA."
#Modified code that puts results into a variable and formats results into list format
$output += $objs | fl Name,Path,@{ Label = "Size(GB)" ; Expression = { "{0:N2}" -f ($_.Size/1024/1024/1024) } },@{ Label = "FreeSpace(GB)" ; Expression = { "{0:N2}" -f ($_.FreeSpace/1024/1024/1024) } },@{ Label = "UsedSpace(GB)" ; Expression = { "{0:N2}" -f ($_.UsedSpace/1024/1024/1024) } },@{ Label = "PercentFree" ; Expression = { "{0:N2}" -f ($_.PercentFree) } } | out-string

#Email results
$SmtpClient = New-Object System.Net.Mail.SmtpClient
$MailMessage = New-Object System.Net.Mail.MailMessage
$SmtpClient.Host = $SmtpServer
$MailMessage.From = $SmtpFrom
Foreach ($address in $smtpTo)
$MailMessage.Subject = $SmtpSubject
#$MailMessage.IsBodyHTML = $true
$MailMessage.Body = $output


Get Last Reboot Date and Time – PowerShell

One of the most common questions asked when working with projects either for patching or testing or any other reason is when servers last rebooted. I have found the WMI class which will give us what we need.


So what can I get out of this class?

Get-WMIObject Win32_OperatingSystem | Get-Member

Get-CimInstance Win32_OperatingSystem | Get-Member

There is a difference we will see. Look at the definitions. This will reflect to their output.



and LastBootUpTime what we need and we need to pull this info out of here;

Get-WMIObject -ClassName win32_OperatingSystem | select csname, lastbootuptime

csname                 lastbootuptime
——                  ————–
USER-PC             20160707090526.982130+060


Get-CimInstance -ClassName win32_OperatingSystem | select csname, lastbootuptime

csname                  lastbootuptime
——                   ————–
USER-PC               7/7/2016 9:05:26 AM

So looks like we need to convert the time if you use Get-WMIObject

>$wmi = gwmi win32_operatingsystem


$LastBootUpTime = Gwmi Win32_OperatingSystem -Comp server01 | Select -Exp LastBootUpTime

>gwmi win32_operatingsystem | %{ $_.ConvertToDateTime($_.LastBootUpTime) }


>$BootTime = Invoke-Command -Cn server01, server02`
  -Command { (gwmi win32_operatingsystem).lastbootuptime }
>$BootTime | foreach { ([wmi]'').ConvertToDateTime($_) }

Why do you need to go through these conversions, just use Get-CimInstance...

>$BootTimes = Get-CimInstance -Cn server1, server2-Class Win32_OperatingSystem |
    Select PSComputerName, LastBootUpTime

>$BootTimes | Format-Table -AutoSize

PSComputerName LastBootUpTime       
-------------- --------------       
server1        8/18/2016 15:40:32 PM 
server2        8/19/2016 20:50:53 PM
 If you have got a few servers put them in a txt file  (serverlist.txt) and use;

$compname = Get-Content -Path C:\serverlist.txt
foreach ($comp in $compname) {
    Get-WmiObject win32_operatingsystem -ComputerName $comp | '
select CSName, @{LABEL='LastBootUpTime';'



Getting PowerShell 5 Running on Windows 7 and Server 2008 R2

PowerShellMagazine - (wallpaper) - KEEP CALM.cdr

The two biggest features in PowerShell 5.0 are Windows PowerShell OneGet and a new module for managing layer 2 network switches. Other features include updates for Desired State Configuration (DSC), the PowerShell console, and the Integrated Scripting Environment (ISE).

OneGet is the PowerShell team’s answer to the complexity of application installation. By using the Chocolately repository, applications can be installed, updated, or removed with command line precision.OneGet can potentially let you automate complex application installations, simplify network shares, and make reinstallation much easier. Imagine creating a OneGet script that installs all of your favorite applications automatically.

Windows Server 2012 R2 introduced native management of layer 2 network switches. When you have Windows Server certified equipment, you can use SCVMM 2012 R2 to manage the hardware. There are times when you might need to manage equipment with a PowerShell script. WMF 5.0 introduces that ability.

The new module, Network Switch, contains 19 cmdlets that allow you to do the following in a CIM session:

  • Investigate the switch and available features with the Get-NetworkSwitchFeature cmdlet.
  • Configure specific switch features or enable/disable switch features.
  • Manage VLANs with the NetworkSwitchVLAN cmdlet set (Get, New, Disable, Enable, Remove, Set).
  • Save or restore switch configurations.

So what is needed for getting PowerShell 5.0 on a Windows 7 or Server 2008 R2?

  1. You must have SP1.  If you are using source media that does not have it slip-streamed then you’ll need to apply it before getting started.  Most cloud based VMs should have SP1 on Server 2008 already.
  2. You must have .NET 3.5.1 or later to install PowerShell 4.  However, the PowerShell 4 installer does not block you if you don’t have it.  It successfully installs and then when you reboot you are still on your previous version (2 or 3).  You have to uninstall the PowerShell 4 Windows Update and then install .NET 4.5.1 and then reinstall PowerShell 4.  If you use the quick config below – this is detected for you and you are instructed to issue a chocolatey command to easily pull .NET 4.5.1 onto your system.
  3. You must first install PowerShell / Windows Management Framework WMF 4 before you can install PowerShell / WMF 5.  This is an interim dependency that the PowerShell team is planning to eliminate for the production release.  However if you try to install PowerShell 5 without 4 present, you just get a standard Windows Update WUSA error “This Update is not applicable to your system” (Error: 2149842967 or 0x80240017).  PowerShell 5 installs via one of the following windows updates:KB3055381, KB3055377 or KB2908075. The below quick config also detects this situation and installs PowerShell 4 and instructs you to reboot and re-run the quick config.
  4. Finally you can install PowerShell 5.


Creating a file share in AZURE

Microsoft Azure offers fully managed file shares in the cloud. Because Azure File Storage exposes file shares using the Server Message Block 3.0 (SMB) protocol, the predominantly used file share protocol for existing on-premises applications, it simplifies moving your existing applications to the cloud, and because Azure File Storage allows applications to mount file shares from anywhere in the world, your on-premises applications can take advantage of cloud storage without change. Azure File Storage also implements REST API protocol, which enables you to develop modern applications that integrate with existing applications. And Microsoft Azure File Storage has got new features like; SMB 3.0 support, includes encryption and persistent handles, a new browser-based file explorer in the Azure portal,  Azure Storage Metrics for Azure File storage an also the ability to mount Azure File Storage file shares from outside of Azure datacenters.

So how to create a file share; (GUI or Powershell), let’s try Powershell…

Choose your storage account… We will need the name for our powershell cmdlets;


And then click on the storage account to go on CONFIGURE .


At the very bottom click on Manage Access Keys to get your key;


We are interested in the Primary Access Key, and click on the little file icon next to it to copy. This is the long key such as


Next is we need to run our Powershell cmdlets;

$storageaccount = “msenelstorage01

$storageaccount_key = “b8VW6dmfugxsrNyF/TkHO9lkA0vSPs4jEos0w+KWEBJzPC0OBFStObAuUwzNJWUkT8Qs5AdUJsHGUiCYqbcjSw==

$sharename = “Logs”

$storageaccount_context = New-AzureStorageContext $storageaccount $storageaccount_key

$create_share = New-AzureStorageShare $sharename -Context $storageaccount_context

And let’s run this….

PS C:\Users\murat.senel> $storageaccount = “msenelstorage01”

$storageaccount_key = “b8VW6dmfugxsrNyF/TkHO9lkA0123456789jEos0w+KWEBJzPCFStObAuUwzNJWUkT8Qs5AdUJsHGUiCYqbcjSw==”

$sharename = “logs”

$storageaccount_context = New-AzureStorageContext $storageaccount $storageaccount_key

$create_share = New-AzureStorageShare $sharename -Context $storageaccount_context

PS C:\Users\murat.senel>

In the storage account, you can see the URL for file storage;



Now we can only check this on the Preview Portal as GUI;

Go to the Portal;


Click on the storage account that you used;


Click on the Files >


There it is, our Logs file share created….

The second method might be easier if you don’t want to use Powershell…

You can basically go to the File Share tab and  on the top you will see New Share tab.


What else can we show with a file share ?

Lets’ create a directory (called Server001Logs) in the Logs share;

PS C:\Users\murat.senel> New-AzureStorageDirectory -Share $create_share -Path Server001Logs

Directory: https://msenelstorage01.file.core.windows.net/logs

Type                Length             Name                                                                                                                 —-                ——             —-                                                                                                                    DIR                                    Server001Logs

And upload a file in to that directory…

Set-AzureStorageFileContent -Share $create_share -Source C:\MyScripts\serverlist.txt -Path Server001Logs

Let’s check them on the Portal ….


Directory is there .. And our uploaded file is …… >


also there…. 😉

Uploading a VHD file to Azure Storage

Get your Azure Subscription details first….

PS C:\Users\user> Get-AzureRmSubscription

SubscriptionName : Free Trial

SubscriptionId   : ba6d424f-f1d9-40c1-be8d-123f6aad8a7b

TenantId         : dc608e75-2124-48be-9dbc-7a248dc51fb2

State            : Enabled


PS C:\Users\user> Get-AzureRmSubscription | ft -Property SubscriptionName



Free Trial                                                                                                                                        

PS C:\Users\user> Get-AzurePublishSettingsFile

PS C:\Users\user> Import-AzurePublishSettingsFile

cmdlet Import-AzurePublishSettingsFile at command pipeline position 1

Supply values for the following parameters:

(Type !? for Help.)

PublishSettingsFile: “C:\book\Free Trial-2-20-2016-credentials.publishsettings”

Id          : ba6d424f-f1d9-40c1-be8d-123456789

Name        : Free Trial

Environment : AzureCloud

Account     : B62082C30D0DA9850123456789

State       :

Properties  : {[Default, True]}

We will need our storage account details such as Name (Label)

PS C:\Users\user> Get-AzureStorageAccount | Format-Table -Property Label







Select your Storage Account

PS C:\Users\user> $storageAccountName = ‘llportalvhds9storage02 ‘

Set-AzureSubscription -SubscriptionName “Free Trial” -CurrentStorageAccountName $storageAccountName

PS C:\Users\user> Get-AzureStorageContainer

Blob End Point: https://llportalvhds9storage02 .blob.core.windows.net/


Name                                           PublicAccess         LastModified

—-                                             ——-                   ———-

vhds                                                 Off                       2/18/2016 11:21:41 AM +00:00

I have a copy of Nano server VHD. I think this is the smallest one I can use (543MB). Don’t forget you will be copying your vhd in to the cloud and it will take some time.

 PS C:\Users\user> $LocalVHD = “C:\book\Nano2016.vhd”

$AzureVHD = “https://llportalvhds9storage02 .blob.core.windows.net/vhds/Nano2016.vhd&#8221;

Add-AzureVhd -LocalFilePath $LocalVHD -Destination $AzureVHD





After finished uploading…..





Go to Storage in Azure Portal,


Find your Storage Account and click on it,


Go to Containers and click on vhds, you should be able to see your vhd file.


Making a copy of the VHD file;

It is always good practice to have a copy of some of your vhds if they are critical to your business. Otherwise you will go through everything from the beginning to upload them again. So;

Go back to Storage and at the bottom of the screen choose Manage Access Keys..


Get the storage account name and primary access key for our powershell cmdlet; copy them on to notepad so you can reuse them.


#Storage account needs to be authenticated

$context = New-AzureStorageContext -StorageAccountName “llportalvhds9storage02″`

 -StorageAccountKey “abcdefghijklmnopqrstuvyz”  -Protocol Http

$containername = “vhds”

$blobname = “Nano2016.vhd”

#From storage we need to get the VHD’s blob

$blob = Get-AzureStorageBlob -Context $context -Container $containername -Blob $blobname

$uri = “https://llportalvhds9storage02 .blob.core.windows.net/vhds/Nano2016.vhd&#8221;

Start-AzureStorageBlobCopy -SrcUri $uri -DestContext $context -DestContainer $containername -DestBlob “Nano2016.vhd-copy.vhd”

To check it, either go to Azure Portal > Storage > Click on the Storage Account > vhds


Or use Powershell to check it if it is in our container (vhds);

$context = New-AzureStorageContext -StorageAccountName “llportalvhds9g0s6kqhgqxw”`

 -StorageAccountKey “abcdefghijklmnopqrstuvyz”  -Protocol Http

$containername = “vhds”

$blobcopyname = ” Nano2016.vhd-copy.vhd”


#From storage we need to get the VHD’s blob

$blobcopy = Get-AzureStorageBlob -Context $context -Container $containername`

-Blob $blobcopyname




Creating an image from a VHD file

 Go to Virtual Machines in Azure Portal and choose Images;


 Click on the “Create an image”



Now you can create virtual machine using your image….


Creating a Disk from a VHD file

Go to Virtual Machines in Azure Portal and choose Disks;


At the bottom of the screen choose Create….


If it is OS disk make sure you click “This VHD contains an operating system”..


And again you can use this disk to create your virtual machines….


Using  PowerShell;

$AzureVHD = “https://llportalvhds9storage02 .blob.core.windows.net/vhds/Nano2016.vhd”

 Add-AzureDisk -DiskName ‘NanoOSDisk’ -MediaLocation $AzureVHD `
-Label ‘Nano Server 2016 OS Disk’ -OS Windows