RSS

Monthly Archives: June 2015

Thoughts on enabling Microsoft Antimalware extension on Azure virtual machines…

Recently, I was investigating in managing the Microsoft Antimalware extension on Azure virtual machines.

As we all know, the Microsoft Antimalware extension can be enabled when creating a new Azure virtual machine in the Azure portal. While enabling the Microsoft Antimalware extension can be enabled there, only the default settings will be applied. This might work in most scenario’s but company policy may require customization when specified, this may be extended to customizing the extension for specific server roles or even desktops.

It became clear that the only way to customize the configuration is using Azure PowerShell.

NOTE: More customization is also possible in the ‘new’ portal available at http://portal.azure.com . At his time of writing this portal is still in Preview though so it is not support.

After checking out the cmdlet reference for Azure, I found the Set-AzureVMMicrosoftAntimalwareExtension cmdlet. More information on this cmdlet is available at https://msdn.microsoft.com/en-us/library/dn771716.aspx

After reading the article I noticed that .json files can be used to provision a configuration for the extension. This brings a new challenge: what configuration should be in the .json file for a specific server role.

If an existing System Center Configuration Manager 2012 or newer infrastructure is available and the Endpoint Protection Point is enabled and used, then either existing configurations or the Endpoint Protection templates can be used. The trick is to read a template and ‘translate’ it into a .json file.

I decided to use the Domain Controller template as a reference. After analyzing the template .xml file, the resulting .json may look like this:

{
 “AntimalwareEnabled”: true,
 “RealtimeProtectionEnabled”: true,
 “ScheduledScanSettings”:
 {
   “isEnabled”: true,
    “day”: 7,
    “time”: 120,
    “scanType”: “Full”
 },
    “Exclusions”:
 {
     “Extensions”: “.pol;.chk;.edb;.sdb;.pat”,
     “Paths”: “%systemroot%\\NTDS\\Ntds.dit;%systemroot%\\NTDS\\EDB*.log;%systemroot%\\NTDS\\Edbres*.jrs;%systemroot%\\SYSVOL\\domain\\DO_NOT_REMOVE_NtFrs_PreInstall_Directory\\;%systemroot%\\SYSVOL\\staging;%systemroot%\\SYSVOL\\staging areas;%systemroot%\\SYSVOL\\sysvol”,
     “Processes”: “”
 }
}

Keep in mind though that using wildcards in the .json file is not recommended by Microsoft as stated in the cmdlet reference page for the Set-AzureVMMicrosoftAntimalwareExtension cmdlet.

This method allows administrators to create multiple .json files for specific server roles and specify them when enabling the extension.

Feel free to use this method yourself. As always, try this out in a test environment or separate subscription used for testing purposes.

Hope this helps…

 

Using PowerShell for bulk Configuration Manager 2012 SP1 (or newer) Client installation…

You might be in the situation that the Configuration Manager Client needs to be installed on many machines. However, automatic client deployment using Client Push is not allowed not preferred for whatever technical/financial/political reason. Browsing in the Console and select a lot of discovered machines may be a very frustrating action. It may also be extremely error prone, especially when you already know which machines need to have a Configuration Manager Client (they’re most like not deployed by using OSD).

Fortunately, we can use PowerShell to have the Configuration Manager Client installed. What makes it even better is that a single cmdlet is needed to get the job done: Install-CMClient

You can find more info for the Install-CMClient cmdlet at the following location:

https://technet.microsoft.com/library/jj821865(v=sc.20).aspx

If you already know which machines need the Configuration Manager Client to be installed, then you can put them in a .csv file. This allows you to create a PowerShell script that reads each object and run the cmdlet for each object. A script to get the job done might look like this:

#
# Script name: Install_Client_Bulk.ps1
#
# Purpose: Installs the ConfigMgr client on multiple machines, uses .csv as input
#
# Author: Marc Westerink
#
# Reference: http://technet.microsoft.com/library/jj821865(v=sc.20).aspx
#

#Create the required ‘global’ variables
$ConfigMgrModulePath=“D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1″
$ConfigMgrSiteCode=“P01:”
#Connecting to site
Import-Module $ConfigMgrModulePath
Set-Location $ConfigMgrSiteCode

#Initiate Client Installation(s)

Import-CSV E:\Install\CMHostName.csv | %{

$SiteCode=”P01″

Install-CMClient -SiteCode $SiteCode -AlwaysInstallClient $True -IncludeDomainController $True -DeviceName $_.CMHostName

}

 

Running this script has one downside. Since a lot of machines will be instructed to have the client installed, you will see a lot of entries in the ccm.log file so monitoring its progress might be challenging. So therefore I recommend testing the script with only a few entries in the .csv file (as little as two) to verify everything’s working as expected…

 

Using PowerShell to copy an Azure virtual machine to a different subscription…

Recently, one of my customers faced a challenge that requires moving a bunch of Azure virtual machines to a different subscription.

I was happy to investigate the issue because it is something I can always use myself or in the future. We all know this is something the Azure portal simply doesn’t facilitate, so a PowerShell script is required. One of the first things I did was checking out if someone has done this in the past which allows me to reuse it. Fortunately, I found the following article:

http://blogs.msdn.com/b/microsoft_press/archive/2014/01/29/from-the-mvps-copying-a-virtual-machine-from-one-windows-azure-subscription-to-another-with-powershell.aspx

As this article provides a ‘ready-to-use’ script, I decided to analyze what the script is actually doing so that I understand what I’m doing as well (something I consider very important).

The following workflow describes what the script actually does:

  1. It shuts down the machine
  2. It gets the VM information specified and creates an export file of the VM configuration
  3. The attached .vhd file(s) is/are copied to the blob storage of the destination subscription
  4. A new VM is created using the export file created before

My first thought was: great, exactly what I’m looking for since it will meet the requirement of moving the VM. Since the VM is not deleted at the source subscription I always have the existing scenario.

Unfortunately, the script has three flaws that will make copying the VM fail:

  1. Line 80 contains a mistake. The line I’m talking about is: Get-AzureSubscription -Current | Set-AzureSubscription -CurrentStorageAccountName $destStorageName
  2. The script assumes that the source and destination Virtual Network (VNet) names are equal
  3. The script assumes it is run from the Azure PowerShell module, but what if you want to run it in the ISE?

The first flaw is easy to fix with something like this: Set-AzureSubscription –SubscriptionName $destSubscription -CurrentStorageAccountName $destStorageName

No need to get the current subscription and pipe it the Set-AzureSubscription cmdlet…

To fix the third flaw, all that needs to be done is to use an Import-Module cmdlet to load the Azure PowerShell module. It also allows to use the Add-AzureAccount cmdlet that is at least Co-Administrator of both subscriptions used. I oppose adding credentials in the script because I consider it a serious security risk. Therefore, I prefer to use the ISE have some more control of the lines I want to run separately.

If the source and destination Virtual Networks have different names, then a little bit more work is required. This means the workflow will look like this:

  1. Import the Azure PowerShell module and log on to Azure
  2. Specify source and destination subcriptions, VNets and the VM info that needs to be copied
  3. Shutdown the machine specified and create an export file of the VM configuration
  4. Copy the attached .vhd files to the destination blob storage of the destination subscription
  5. Rename the VNet name in the export .xml file and create a second .xml that contains the destination VNet name
  6. Create a VM with the secondly created .xml file

In the original script the VM creation part looks like this:

$vmConfig = Import-AzureVM -Path $vmConfigurationPath

New-AzureVM -ServiceName $destServiceName -Location $location -VMs $vmConfig -WaitForBoot

 

I modified the script by changing that part to this:

get-content $vmConfigurationPath | % { $_ -replace $sourceVNet, $destVNet } | set-content $vmConfigurationPathnew

$vmConfig = Import-AzureVM -Path $vmConfigurationPathnew

New-AzureVM -ServiceName $destServiceName -VMs $vmConfig –WaitForBoot

 

After modifying the script I was able to successfully copy an Azure VM to a new subscription.

While the script was running I used the opportunity to look a bit deeper on how to approach this scenario. I was considering to automate it a little further by running the script in a loop to allow multiple VM’s to be copied. I must admit that an action such a this is something that must be carefully planned with the tenant who uses the virtual machines that need to be copied. Since big changes always involve a lot of work I recommend testing things like these first and copy the virtual machines in a more controlled fashion.

Last but not least, here’s my full sample script. You can discover the differences between the original one and mine (you may need to check for unexpected ‘enters’):

Import-Module “C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Azure.psd1”

Add-AzureAccount

 

$sourceSubscription = “EXISTING SUBSCRIPTION NAME”

$destSubscription = “NEW SUBSCRIPTION NAME”

 

Select-AzureSubscription -SubscriptionName $sourceSubscription

 

#Get-AzureVM

 

$vmName = “YOUR_VM_NAME”

$serviceName = “CLOUD_SERVICE_NAME”

$destServiceName = “NEW_CLOUD_SERVICE_NAME”

$workingDir = (Get-Location).Path

 

$sourceVm = Get-AzureVM –ServiceName $serviceName –Name $vmName

$vmConfigurationPath = $workingDir + “\exportedVM.xml”

$vmConfigurationPathnew = $workingDir + “\exportedVM_new.xml”

$sourceVm | Export-AzureVM -Path $vmConfigurationPath

 

$sourceVNet = “SOURCE_SUBNET_NAME”

$destVNet = “DESTINATION_SUBNET_NAME”

 

$sourceOSDisk = $sourceVm.VM.OSVirtualHardDisk

$sourceDataDisks = $sourceVm.VM. DataVirtualHardDisks

 

$sourceStorageName = $sourceOSDisk.MediaLink.Host -split “\.” | select -First 1

$sourceStorageAccount = Get-AzureStorageAccount –StorageAccountName $sourceStorageName

$sourceStorageKey = (Get-AzureStorageKey -StorageAccountName $sourceStorageName).Primary

 

 

 

Stop-AzureVM –ServiceName $serviceName –Name $vmName -Force

 

Select-AzureSubscription -SubscriptionName $destSubscription

 

$location = $sourceStorageAccount.Location

 

$destStorageAccount = Get-AzureStorageAccount | ? {$_.Location -eq $location} | select -first 1

if ($destStorageAccount -eq $null)

{

$destStorageName = “TARGET_STORAGE_ACCOUNT”

New-AzureStorageAccount -StorageAccountName $destStorageName -Location $location

$ destStorageAccount = Get-AzureStorageAccount -StorageAccountName $destStorageName

}

$destStorageName = $destStorageAccount.StorageAccountName

$destStorageKey = (Get-AzureStorageKey -StorageAccountName $destStorageName).Primary

 

$sourceContext = New-AzureStorageContext –StorageAccountName $sourceStorageName `

-StorageAccountKey $sourceStorageKey

$destContext = New-AzureStorageContext –StorageAccountName $destStorageName `

-StorageAccountKey $destStorageKey

 

if ((Get-AzureStorageContainer -Context $destContext -Name vhds -ErrorAction SilentlyContinue) -eq $null)

{

New-AzureStorageContainer -Context $destContext -Name vhds

}

 

$allDisks = @($sourceOSDisk) + $sourceDataDisks

$destDataDisks = @()

foreach($disk in $allDisks)

{

$blobName = $disk.MediaLink.Segments[2]

$targetBlob = Start-CopyAzureStorageBlob -SrcContainer vhds -SrcBlob $blobName `

-DestContainer vhds -DestBlob $blobName `

-Context $sourceContext -DestContext $destContext -Force

Write-Host “Copying blob $blobName”

$copyState = $targetBlob | Get-AzureStorageBlobCopyState

while ($copyState.Status -ne “Success”)

{

$percent = ($copyState.BytesCopied / $copyState.TotalBytes) * 100

Write-Host “Completed $(‘{0:N2}’ -f $percent)%”

sleep -Seconds 5

$copyState = $targetBlob | Get-AzureStorageBlobCopyState

}

If ($disk –eq $sourceOSDisk)

{

$destOSDisk = $targetBlob

}

Else

{

$destDataDisks += $targetBlob

}

}

 

Add-AzureDisk -OS $sourceOSDisk.OS -DiskName $sourceOSDisk.DiskName -MediaLocation $destOSDisk.ICloudBlob.Uri

foreach($currenDataDisk in $destDataDisks)

{

$diskName = ($sourceDataDisks | ? {$_.MediaLink.Segments[2] -eq $currenDataDisk.Name}).DiskName

Add-AzureDisk -DiskName $diskName -MediaLocation $currenDataDisk.ICloudBlob.Uri

}

 

Set-AzureSubscription –SubscriptionName $destSubscription -CurrentStorageAccountName $destStorageName

 

get-content $vmConfigurationPath | % { $_ -replace $sourceVNet, $destVNet } | set-content $vmConfigurationPathnew

 

$vmConfig = Import-AzureVM -Path $vmConfigurationPathnew

New-AzureVM -ServiceName $destServiceName -VMs $vmConfig -WaitForBoot

 
 
Steve Thompson [MVP]

The automation specialist

Boudewijn Plomp

Cloud and related stuff...

Anything about IT

by Alex Verboon

MDTGuy.WordPress.com

Deployment Made Simple

Modern Workplace

Azure, Hybrid Identity & Enterprise Mobility + Security

Daan Weda

This WordPress.com site is all about System Center and PowerShell

IT And Management by Abheek

Microsoft certified Trainer -Abheek

Heading To The Clouds

by Marthijn van Rheenen