Archive | Azure

Code to query Azure Load Balancer Metrics to verify Availability (VipAvailability )

This one was fun to put together.

I wanted to write code to query the status of an Azure Load Balancer. I couldn’t find much out there. This code query’s the Azure Load Balancer’s Metrics for VipAvailability – through the REST API. If it returns 100 then are good to go. Anyting else, then there may be a issue. You can query any metric, and you can set a time range, I am just looking at the last min.

Note: This is for a Standard Load Balancer, not Basic.

Some of the Metrics Available:

VipAvailability : Average count of availability of VIP endpoints, based on probe results.
DipAvailability : Average count of availability of DIP endpoints, based on probe results.
ByteCount : Total number of bytes processed per front-end.
PacketCount : Total number of packets processed per front-end.
SynCount : Total number of SYN packets received.
SnatConnectionCount : Total number of new SNAT connections, that is, outbound connections that are masqueraded to the Public IP address front-end.

And the same metrics are often referred to by different names (this was confusing to me):

value               localizedValue                
-----               --------------                
VipAvailability     Data Path Availability        
DipAvailability     Health Probe Status           
ByteCount           Byte Count                    
PacketCount         Packet Count                  
SYNCount            SYN Count                     
SnatConnectionCount SNAT Connection Count         
AllocatedSnatPorts  Allocated SNAT Ports (Preview)
UsedSnatPorts       Used SNAT Ports (Preview) 

Here is the code (bouns: BASH/cURL too) to find the VipAvaiablity of Azure Load Balancers:

$SubscriptionId = "$($env:SubscriptionId)"
$TenantId       = "$($env:TenantId)" 
$ClientID       = "$($env:ClientID)"      
$ClientSecret   = "$($env:ClientSecret)"  
$TenantDomain   = "$($env:TenantDomain)" 
$loginURL       = "https://login.microsoftonline.com/$TenantId/oauth2/token"
$resource      = "https://management.core.windows.net/" 
$resourceGroupName = "eastUS-01"
$body           = @{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth          = Invoke-RestMethod -Method Post -Uri $loginURL -Body $body
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}

$start=((Get-Date).AddMinutes(-1)).ToUniversalTime().ToString("yyy-MM-ddTHH:mm:00Z")
$end=(Get-Date).ToUniversalTime().ToString("yyy-MM-ddTHH:mm:00Z")
$filter = "(name.value eq 'VipAvailability') and aggregationType eq 'Average' and startTime eq $start and endTime eq $end and timeGrain eq duration'PT1M'"
$url = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Network/loadBalancers/jemurphyLB01/providers/microsoft.insights/metrics?`$filter=${filter}&api-version=2016-09-01"
$results=Invoke-RestMethod -Uri $url -Headers $headerParams -Method Get
$results.value | select -ExpandProperty data | select timestamp,average
SUBSCRIPTIONID=""
RESOURCEGROUPNAME=""
CLIENTID=""
CLIENTSECRET=""
TENANTID=""
RESOURCEGROUPNAME=""
LBNAME=""

LOGINURL="https://login.microsoftonline.com/$TENANTID/oauth2/token"
RESOURCE="https://management.core.windows.net/" 

TOKEN=$(curl --silent --request POST $LOGINURL --data-urlencode "resource=https://management.core.windows.net" --data-urlencode "client_id=$CLIENTID" --data-urlencode "grant_type=client_credentials" --data-urlencode "client_secret=$CLIENTSECRET" | jq -r '.access_token')

STARTTIME=$(date -u +'%Y-%m-%dT%H:%M:00' --date='-1 min')
ENDTIME=$(date -u +'%Y-%m-%dT%H:%M:00')

FILTER="(name.value eq 'VipAvailability') and aggregationType eq 'Average' and startTime eq $STARTTIME and endTime eq $ENDTIME and timeGrain eq duration'PT1M'"
URL="https://management.azure.com/subscriptions/$SUBSCRIPTIONID/resourceGroups/$RESOURCEGROUPNAME/providers/Microsoft.Network/loadBalancers/$LBNAME/providers/microsoft.insights/metrics"

RESULTS=$(curl -s -G --header "authorization: Bearer $TOKEN" --data-urlencode "\$filter=$FILTER" --data-urlencode "api-version=2016-09-01" $URL | jq .value[].data[].average)

echo "$RESULTS"

I think the hardest part was trying to get the date and time in the right format. Why is that so hard?

This HAS to be helpful to some one!

0

Using Azure Traffic Manager with IP White-listed resources

The question was, how can you use Azure Traffic Manager if the destinations are restricted with IP white lists?
This is the only way I could find:

  1. There is a blob that contains the source IPs of the probes. Here is the file, And here is the reference
  2. This list would need to be queried often because I couldn’t find any indication of when it would be updated
  3. I wrote PowerShell to parse the results and put it into an NSG.

$RGName= "Your RG Name"
$NSGName = "Your NSG Name"
$NSGRuleName = "Your Rule Name"
$Priority = 120
$DestinationPortRange = 443
$url="https://azuretrafficmanagerdata.blob.core.windows.net/probes/azure/probe-ip-ranges.json"
$results=Invoke-RestMethod -Uri $url
$allAddresses=@()
foreach ($address in $results.ipv4_prefixes){
$allAddresses += $address.ip_prefix
}
# for some reason, get-AzureRmNetworkSecurityRuleConfig errors out if there is no matchin name
# could use a try - catch
if  ((Get-AzureRmNetworkSecurityGroup -ResourceGroupName $RGName -Name $NSGName | get-AzureRmNetworkSecurityRuleConfig -Name $NSGRuleName -ErrorAction SilentlyContinue) -eq $null){
# Creating RUle
Get-AzureRmNetworkSecurityGroup -ResourceGroupName $RGName -Name $NSGName | `
Add-AzureRmNetworkSecurityRuleConfig -Name $NSGRuleName -Description "Allow Probe from ATM" -Access Allow -Protocol Tcp -Direction Inbound -Priority $Priority -SourceAddressPrefix $allAddresses -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange $DestinationPortRange | Set-AzureRmNetworkSecurityGroup
}
else {
# Updating Rule
Get-AzureRmNetworkSecurityGroup -ResourceGroupName $RGName -Name $NSGName | `
Set-AzureRmNetworkSecurityRuleConfig -Name $NSGRuleName -Description "Allow Probe from ATM" -Access Allow -Protocol Tcp -Direction Inbound -Priority $Priority -SourceAddressPrefix $allAddresses -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange $DestinationPortRange | Set-AzureRmNetworkSecurityGroup
}

Hope that helps.

0

PowerShell to move a VM to a new Log Analytics WorkSpace

This code uninstalls the Microsoft Monitoring agent and re-installs it to a new WorkSpace.


# change your VM Name and it's resource group
$vm = get-azurermvm -VMName YourVMName -ResourceGroupName VMResourceGroup
Remove-AzureRmVMExtension -ResourceGroupName $vm.ResourceGroupName -VMName $vm.Name -Name MicrosoftMonitoringAgent -Force
# put in your new workspaceId & workspaceKey
$workspaceId = "NewWorksSpaceID"
$workspaceKey = "SupaSecretKey"

$PublicSettings = @{"workspaceId" = $workspaceId;"stopOnMultipleConnections" = $false}
$ProtectedSettings = @{"workspaceKey" = $workspaceKey}

Set-AzureRmVMExtension -ExtensionName "MicrosoftMonitoringAgent" -ResourceGroupName $vm.resourcegroupname -VMName $vm.name `
-Publisher "Microsoft.EnterpriseCloud.Monitoring" `
-ExtensionType "MicrosoftMonitoringAgent" `
-TypeHandlerVersion 1.0 `
-Settings $PublicSettings `
-ProtectedSettings $ProtectedSettings `
-Location $vm.Location

Nothing special, just thought I would put it here. Mayby it will help someone?

3

Use the REST API to create a new Project in Azure DevOps

As the title says, I wanted to create a new project in VSTS / Azure DevOps, whatever you want to call it. Here is the code to do that. You need a Personal Access Token to authenticate with.

$User="[email protected]"
$PAT="YourPAT"
$Organization="YourOrg"
$base64authinfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User, $PAT)))
$url="https://dev.azure.com/$Organization/_apis/projects?api-version=5.1-preview.4"
$body = @"
{
  "name": "FabrikamTravel",
  "description": "Frabrikam travel app for Windows Phone",
  "capabilities": {
    "versioncontrol": {
      "sourceControlType": "Git"
    },
    "processTemplate": {
      "templateTypeId": "6b724908-ef14-45cf-84f8-768b5384da45"
    }
  }
}
"@
Invoke-RestMethod -Method POST -ContentType application/json -Uri $url -Headers @{Authorization=("Basic {0}" -f $base64authinfo)} -Body $Body

Hope that helps someone?

1

Using PowerShell to query Azure Log Analytics via the REST API

I wanted to pull some data out of Azure Log Analytics using PowerShell and the REST API. Here is the code to Pull all errors in the Application event logs on VMs that are pushing their logs into Log Analytics via MicrosoftMonitoringAgent.

Hopefully this may help someone:

$SubscriptionId = "$($env:SubscriptionId)"
$TenantId       = "$($env:TenantId)" 
$ClientID       = "$($env:ClientID)"      
$ClientSecret   = "$($env:ClientSecret)"  
$TenantDomain   = "$($env:TenantDomain)" 
$loginURL       = "https://login.microsoftonline.com/$TenantId/oauth2/token"
$resource       = "https://api.loganalytics.io"         

$body           = @{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth          = Invoke-RestMethod -Method Post -Uri $loginURL -Body $body
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}

$Workspacename="Your WS Name"
$WorkspaceId="Your WS ID"

$url="https://api.loganalytics.io/v1/workspaces/$WorkspaceId/query"
$body = @{query = 'Event | where EventLog == "Application" | order by TimeGenerated asc | project Computer,EventLog,Source,EventLevelName,EventID,RenderedDescription,TimeGenerated'} | ConvertTo-Json
$webresults=Invoke-RestMethod -UseBasicParsing -Headers $headerParams -Uri $url -Method Post -Body $body -ContentType "application/json"

Notes:

  1. I keep my subscription information in Env Varaibles. It is easier for me to swtich to a different Tenant
  2.  This returns the results in tables. To move the tables into an object look at this person’s code at line 60  https://blog.tyang.org/2017/11/14/searching-oms-using-the-new-search-language-kusto-rest-api-in-powershell/
  3. My interpretation of code in #2
$resultsTable=$webresults.Content | ConvertFrom-Json
$count = 0
foreach ($table in $resultsTable.Tables) {
$count += $table.Rows.Count
}
$results = New-Object object[] $count
$i = 0;
foreach ($table in $resultsTable.Tables) {
    foreach ($row in $table.Rows) {
        # Create a dictionary of properties
        $properties = @{}
        for ($columnNum=0; $columnNum -lt $table.Columns.Count; $columnNum++) {
            $properties[$table.Columns[$columnNum].name] = $row[$columnNum]
        }      
        $results[$i] = (New-Object PSObject -Property $properties)
        $null = $i++
    }
}
$results



0

Removing machines from Azure State Configuration (DSC)

I have been provisioning machines over an over trying to learn all the VM Extensions. One of the extensions that I have been playing with is the DSC extension. Every time I provision with this extension, it adds an additional record into the State Configuration, resulting in many stale machines. I wanted to clear out all the old machines. I couldn’t find a way to do it in PowerShell, so I figure out how to do it via the REST API (and PowerShell).

Here is the code to remove all machines from Azure State Configuration (DSC)

$SubscriptionId = "$($env:SubscriptionId)"
$TenantId       = "$($env:TenantId)" 
$ClientID       = "$($env:ClientID)"      
$ClientSecret   = "$($env:ClientSecret)"  
$TenantDomain   = "$($env:TenantDomain)" 
$loginURL       = "https://login.microsoftonline.com/$TenantId/oauth2/token"
$resource       = "https://management.core.windows.net/"    
$resourceGroupName = "YourResourceGroupName "
$automationAccountsName ="YourAutomationAccountsName "

# get the OAUTH token & prepare header
$body           = @{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth          = Invoke-RestMethod -Method Post -Uri $loginURL -Body $body
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}

# main query to find all the nodes
$url="https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Automation/automationAccounts/$automationAccountsName/nodes?api-version=2018-01-15"
$results=Invoke-RestMethod -Uri $url -Headers $headerParams -Method Get
# Loop through all the nodes and delete them all.
foreach ($node in $($results.value | Select-Object  -ExpandProperty properties | Select nodeid)){
$url="https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Automation/automationAccounts/$automationAccountsName/nodes/$($node.nodeId)?api-version=2018-01-15"
Invoke-RestMethod -Uri $url -Headers $headerParams -Method Delete
}

Notes:

  1. I put all my SPN info into environmental varaibles (easier to switch of needed)
  2. Put in your RG name and Auutomation Account Name
  3. Warning. This will delete all nodes!

HTH

0

My PowerShell scripts to encrypt Azure VM disks

This is my steps that I took from this very long document.

First we need to create a Key vault and then an AAD application, then you connect them. Make note of the output of $aadClientID.

$KeyVaultName="YourName-EastUS"
$ResourceGroupName="Default-EastUS"
$Location="East US"


#Create New KeyVault
New-AzureRmKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName -Location $Location

#Create New AAD Application
$aadClientSecret = "YourLongSecret"
$azureAdApplication = New-AzureRmADApplication -DisplayName "Encryption-EastUS" -HomePage "https://IThinkAnythingCanGoHere" -IdentifierUris "https://IThinkAnythingCanGoHereURi" -Password $aadClientSecret
$servicePrincipal = New-AzureRmADServicePrincipal -ApplicationId $azureAdApplication.ApplicationId
$aadClientID = $azureAdApplication.ApplicationId
$aadClientID
Set-AzureRmKeyVaultAccessPolicy -VaultName $KeyVaultName -ServicePrincipalName $aadClientID -PermissionsToKeys all -PermissionsToSecrets all -ResourceGroupName $ResourceGroupName;
Set-AzureRmKeyVaultAccessPolicy -VaultName $KeyVaultName -EnabledForDiskEncryption

Once that is setup, you can encrypt a VM:

$KeyVaultName="YourName-EastUS"
$ResourceGroupName="Default-EastUS"
$Location="East US"
$vmName="VMNAME"

$aadClientSecret = "YourLongSecret"
$aadClientID = "YouMadeNoteOfThisAbove"
$KeyVault = Get-AzureRmKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName;
$diskEncryptionKeyVaultUrl = $KeyVault.VaultUri;
$KeyVaultResourceId = $KeyVault.ResourceId;

Set-AzureRmVMDiskEncryptionExtension -ResourceGroupName $ResourceGroupName -VMName $vmName -AadClientID $aadClientID -AadClientSecret $aadClientSecret -DiskEncryptionKeyVaultUrl $diskEncryptionKeyVaultUrl -DiskEncryptionKeyVaultId $KeyVaultResourceId;

If you did not make note of your aadClientID, then you run:

get-AzureRmADApplication

And the ApplicationId is what you are looking for.

I forgot how I set this up, so I went back and made some notes, and now I hope this helps someone.

3

My script/procedure to move Hyper-V VMs to Azure

We have been moving resources from ESXi to Hyper-V to Azure. ESXi to Hyper-V is done via the Microsoft Virtual Machine Converter (MVMC). Here is the Checklist/Script/Procedure I have been using to get Hyper-V to Azure.

  1. Once machine is in Hyper-V, make sure the VMs HDs are VHD and not VHDX
  2. Make sure DHCP is set on the VM
  3. Make sure RDP is enabled (ours is set via group policy)
  4. Power down VM
  5. Run the PowerShell below to add the HD (Add-AzurermVhd), and create a new VM in Azure:
Login-AzureRmAccount
$VMName="NAMEOFMACHINE"
$DestinationVMSize="Standard_A1"
$DestinationAvailabilitySet="AvailabilitySetName"
$PrivateIpAddress="192.168.5.55"
$ResourceGroupName="YourResourceGroup"
$DestinationNetworkName="YourNetwork"
$DestinationNetworkSubnet="YourLanSubnet"
$Location="East US2"
$OSType="Windows"
[switch]$DataDisk=$false
$SourceSystemLocalFilePath="C:\PathToYour\VHDs\$($VMName)-System.vhd"
$SourceDataLocalFilePath="C:\PathToYour\VHDs\$($VMName)-Data.vhd"
$DestinationStorageAccountName="yourstorageaccount"
$DestinationSystemDiskUri= "http://$DestinationStorageAccountName.blob.core.windows.net/vhds/$VMName-System.vhd"
$DestinationDataDiskUri= "http://$DestinationStorageAccountName.blob.core.windows.net/vhds/$VMName-Data.vhd"
$DestinationSystemDiskName="$($VMNAME)_SYSTEM.vhd"
$DestinationDataDiskName="$($VMNAME)_DATA01.vhd"
 
Add-AzurermVhd -Destination $DestinationSystemDiskUri -LocalFilePath $SourceSystemLocalFilePath -ResourceGroupName $ResourceGroupName
if ($DataDisk){
Add-AzurermVhd -Destination $DestinationDataDiskUri -LocalFilePath $SourceDataLocalFilePath -ResourceGroupName $ResourceGroupName
}
 
#region Build New VM
$DestinationVM = New-AzureRmVMConfig -vmName $vmName -vmSize $DestinationVMSize -AvailabilitySetId $(Get-AzureRmAvailabilitySet -ResourceGroupName $ResourceGroupName -Name $DestinationAvailabilitySet).Id
$nicName="$($VMName)_NIC01" 
$vnet = Get-AzureRmVirtualNetwork -Name $DestinationNetworkName -ResourceGroupName $ResourceGroupName
$subnet = $vnet.Subnets | where {$_.Name -eq $DestinationNetworkSubnet}
$nic = New-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $ResourceGroupName -Location $Location -SubnetId $Subnet.Id -PrivateIpAddress $PrivateIpAddress
$DestinationVM = Add-AzureRmVMNetworkInterface -VM $DestinationVM -Id $nic.Id
$DestinationSystemDiskUri = $DestinationSystemDiskUri
$DestinationDataDiskUri = $DestinationDataDiskUri
 
If ($OSType -eq "Windows"){
$DestinationVM = Set-AzureRmVMOSDisk -VM $DestinationVM -Name $DestinationSystemDiskName -VhdUri $DestinationSystemDiskUri -Windows -CreateOption attach
if ($DataDisk){
$DestinationVM = Add-AzureRmVMDataDisk -VM $DestinationVM -Name $DestinationDataDiskName -VhdUri $DestinationDataDiskUri -CreateOption attach -DiskSizeInGB $DatDiskSize
}
}
 
New-AzureRmVM -ResourceGroupName $resourceGroupName -Location $Location -VM $DestinationVM

The most important part is to use “-attach” with “Set-AzureRmVMOSDisk”

Hope that helps someone.

0

Hidden or UnDocumented Network Security Group (NSG) default rule in Azure (DNS)

I have been working to get a Citrix Netscaler up and running in Azure. It has not been easy, as all the documentation is for ASM.

Our network configuration has IPSec tunnels going from OnPrem to Azure, and I have created two SubNets in Azure – a DMZ and a LAN. The DMZ has the following Outbound NSG rules (ACLs) for the NetScaler to talk to a LAN SubNet.

Get-AzureRmNetworkSecurityGroup -ResourceGroupName ResourceGroupName | Select SecurityRules -ExpandProperty SecurityRules | where {$_.Direction -eq "Outbound"} | Select Priority,Name,Protocol,SourceAddressPrefix,SourcePortRange,DestinationAddressPrefix,DestinationPortRange,Access | Sort-Object Priority|ft -AutoSize

DMZ Netscaler = 192.10.8.100
LAN DC = 192.10.9.10

Priority Name                           Protocol SourceAddressPrefix SourcePortRange DestinationAddressPrefix DestinationPortRange Access
-------- ----                           -------- ------------------- --------------- ------------------------ -------------------- ------
     101 LDAP_From_NSIP                 TCP      192.10.8.100        *               192.10.9.10              389                  Allow
     102 DNSUDP_From_NSIP               Udp      192.10.8.100        *               192.10.9.10              53                   Allow
     103 DNSTCP_From_NSIP               TCP      192.10.8.100        *               192.10.9.10              53                   Allow
     104 RADIUS_From_NSIP               Udp      192.10.8.100        *               192.10.9.10              1812                 Allow
    4095 Subnet_To_Internet             *        *                   *               Internet                 *                    Allow
    4096 Deny_All_Outbound              *        *                   *               *                        *                    Deny

As you can see, I add a DenyAll at the end even though there is one in the DefaultSecurityRules. I just like to see it there. I find it comforting.

I found that from then Netscaler, I could do a DNS lookup against my OnPrem DC. How can that be?
Rule 101-104 are only for the Azure LAN DC. Then I DenyAll with 4096.
How can the Netscaler look up via the OnPrem DC?
I am DenyingAll!
I was pulling my hair out.

I realized that I had never changed my DNS server settings for my Virtual Network in Azure (I needed it to join the domain for the local DC when I build it!). I forgot to switch it the local Azure LAN DC.

Therefore, even though there is a DenyAll in my NSG rules, there has to be a Hidden or UnDocumented rule that allows queries to the DNS servers listed in the Virtual Network settings.

As soon as I changed the DNS server settings to the local Azure LAN DC, I could no longer query the OnPrem DC.

I understand why it is there. If you put in a DenyAll (like I did), Windows Servers will panic. They do not like it if they can’t access a DNS server.

I think Azure needs to move the DNS server settings down to the SubNet level, since all VMs are DHCP (Reservations). If they do this, a DMZ and LAN can use different DNS server settings, or none at all.

Just something I ran across today.

0