<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Tom Hollander's blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/tomholl/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/tomholl/feed.xml</link>
    <description>Head in the clouds</description>
    <lastBuildDate>Thu, 11 Jan 2018 02:29:46 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Detecting drift between ARM templates and Azure resource groups</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/detecting-drift-between-arm-templates-and-azure-resource-groups</link>
      <pubDate>Mon, 16 Oct 2017 01:53:42 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/?p=2695</guid>
      <description><![CDATA[In DevOps Utopia, all of your Azure resources are deployed from ARM templates using a Continuous...]]></description>
      <content:encoded><![CDATA[In DevOps Utopia, all of your Azure resources are deployed from ARM templates using a Continuous Deployment tool. The ARM templates and parameters files are all stored in source control, so you can go back through the version history to determine what was changed and what was deployed at any given time. And since only your CD service principal has permission to modify resources, you can be sure that nobody made any "rogue" changes outside of the templates and release process.

While we should all aspire to live in DevOps Utopia, chances are you're not quite there yet. Getting to this requires an investment in processes, tools and culture, and different organisations will have different priorities and rates of progress. So a lot of teams are in a situation where they are investing in automation and continuous deployment, but they still have situations where changes are being made manually, for example using the Azure portal.

For teams in this semi-automated state, one common question I hear is how to tell whether their Azure resources that were originally deployed from a template have "drifted" from their original configuration via manual changes. One approach is to check the Azure Activity Log, but this is far from ideal. While the Activity Log does show changes to resources, it's often hard to figure out exactly what was changed and whether the change was made via a template or some other means. Also the logs are only stored for 90 days unless you've explicitly chosen to retain them in another location.

So I decided to build my own script to make this a little easier. The <a href="https://github.com/tomhollander/armconfigurationdrift"><strong>ArmConfigurationDrift</strong> </a>script takes an ARM template and parameters file and compares it to the resources deployed in an existing Azure Resource Group. It will then let you know if any resources are deployed in one location but not the other, or if any parameters differ between the two. The following diagram shows the approach.

<img width="858" height="613" class="alignnone size-full wp-image-2696" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/10/ARMDriftDiagram.png" />

The "expand template" script is particularly interesting. An ARM template can't be directly compared with deployed resources as it will include parameters, variables and functions that need to be expanded before the resource list can be used. I've never seem this documented, but it turns out that you can use the <strong>Test-AzureRmResourceGroupDeployment </strong>cmdlet and capture the "Debug" stream to get an expanded version of the template, as shown in <a href="https://github.com/marcvaneijk/AzureResourceManager/blob/master/expandTemplate.ps1">this sample</a>.

Once you have the expanded template, it's relatively easy to pull the metadata about the resources deployed to a resource group and compare the two. Unfortunately I found that there are a number of cases where the properties in the two collections are not identical even if the resources are the same. To minimise false positives, I only report on differences where the exact same property exists in each collection and has different values. I also normalise the different representations of locations so, for example, "Australia East" and "australiaeast" are treated as the same. Even so, there may still be situations where the tool reports false positives or doesn't detect legitimate differences. If you find any such cases or feel like improving the script, please log an issue or make a pull request on the <a href="https://github.com/tomhollander/armconfigurationdrift">GitHub site</a>.]]></content:encoded>
    </item>
    <item>
      <title>Get Alerts as you approach your Azure resource quotas</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/get-alerts-as-you-approach-your-azure-resource-quotas</link>
      <pubDate>Sun, 11 Jun 2017 05:11:21 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/?p=2645</guid>
      <description><![CDATA[Updated 16/06/2107 to also return Network usage 

Each Azure subscription has a bunch of limits...]]></description>
      <content:encoded><![CDATA[<em><strong>Updated 16/06/2107 to also return Network usage </strong></em>

Each Azure subscription has a bunch of <a href="https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits">limits and quotas</a>. Most of these are "soft" limits, meaning that they can be raised on your request--the limits exist to help with data centre capacity planning and to avoid "bill shock" if you accidentally deploy a lot more than you should have.

You can view your current quotas, usage against those quotas, and request quota increases by logging on to the Azure Portal, selecting your subscription and choosing "Usage and Quotas":

<a href="https://msdnshared.blob.core.windows.net/media/2017/06/AzureQuotas.png"><img width="1024" height="534" class="wp-image-2655 size-large alignnone" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/AzureQuotas-1024x534.png" />
</a>

While you can check this page whenever you want, when you're working on a project you probably have other things to worry about besides checking how you're going against your quotas. But sometimes your deployments can grow quickly, and finding out you've hit limit just as you need to deploy more resources can be a pain, as getting quota increases isn't instantaneous. Would it be great if you could find out when you're close to hitting your quota limits before it's too late?

Luckily this is pretty easy to do, at least for most of your quotas. Azure PowerShell includes the cmdlets <strong>Get-AzureRmVMUsage </strong>and <strong>Get-AzureRmStorageUsage</strong> which will report your current usage and quotas for a range of different resource types. Networking is a bit trickier as it's not included in the PowerShell commands, but the same info can be pulled directly out of the <a href="https://docs.microsoft.com/en-us/rest/api/network/usages">REST API</a>. But you don't want to be responsible for calling these functions to the check the quotas yourself, you want to be notified proactively. You could build your own notification engine, but a better option is to use something that's already good at alerting. There are a bunch of tools that could use, but in this post I'll be using <a href="https://docs.microsoft.com/en-us/azure/log-analytics/log-analytics-overview">OMS Log Analytics</a>.

Before we can use Log Analytics to send alerts based on your Azure quotas, you need to get data into its store. Azure is able to automatically send a bunch of data into OMS, but quota details are not included (at least not as of June 2017). Luckily there is a <a href="https://docs.microsoft.com/en-au/azure/log-analytics/log-analytics-data-collector-api">Log Collector API</a> which lets you write your own data into the Log Analytics repository. I took the PowerShell sample from this page and modified by calling the <strong>Get-AzureRmVMUsage </strong>and <strong>Get-AzureRmStorageUsage </strong>cmdlets and the Network Usage REST API to populate the events. Finally I put this into Azure Automation which lets me easily authenticate to Azure and configure the script to run on a schedule (e.g. once per day). The final script looks like this:

[powershell] Param(
 [string]$omsWorkspaceId,
 [string]$omsSharedKey,
 [string[]]$locations
)

# To test outside of Azure Automation, replace this block with Login-AzureRmAccount
$connectionName = &quot;AzureRunAsConnection&quot;
try
{
    # Get the connection &quot;AzureRunAsConnection &quot;
    $servicePrincipalConnection = Get-AutomationConnection -Name $connectionName         

    &quot;Logging in to Azure...&quot;
    Add-AzureRmAccount `
        -ServicePrincipal `
        -TenantId $servicePrincipalConnection.TenantId `
        -ApplicationId $servicePrincipalConnection.ApplicationId `
        -CertificateThumbprint   $servicePrincipalConnection.CertificateThumbprint 
}
catch {
    if (!$servicePrincipalConnection)
    {
        $ErrorMessage = &quot;Connection $connectionName not found.&quot;
        throw $ErrorMessage
    } else{
        Write-Error -Message $_.Exception
        throw $_.Exception
    }
} 

$LogType = &quot;AzureQuota&quot;

$json = ''

# Credit: s_lapointe  https://gallery.technet.microsoft.com/scriptcenter/Easily-obtain-AccessToken-3ba6e593
function Get-AzureRmCachedAccessToken()
{
  $ErrorActionPreference = 'Stop'
  
  if(-not (Get-Module AzureRm.Profile)) {
    Import-Module AzureRm.Profile
  }
  $azureRmProfileModuleVersion = (Get-Module AzureRm.Profile).Version
  # refactoring performed in AzureRm.Profile v3.0 or later
  if($azureRmProfileModuleVersion.Major -ge 3) {
    $azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
    if(-not $azureRmProfile.Accounts.Count) {
      Write-Error &quot;Ensure you have logged in before calling this function.&quot;    
    }
  } else {
    # AzureRm.Profile &amp;lt; v3.0
    $azureRmProfile = [Microsoft.WindowsAzure.Commands.Common.AzureRmProfileProvider]::Instance.Profile
    if(-not $azureRmProfile.Context.Account.Count) {
      Write-Error &quot;Ensure you have logged in before calling this function.&quot;    
    }
  }
  
  $currentAzureContext = Get-AzureRmContext
  $profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile)
  Write-Debug (&quot;Getting access token for tenant&quot; + $currentAzureContext.Subscription.TenantId)
  $token = $profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId)
  $token.AccessToken
}

# Network Usage not currently exposed through PowerShell, so need to call REST API
function Get-AzureRmNetworkUsage($location) 
{
    $token = Get-AzureRmCachedAccessToken
    $authHeader = @{
    'Content-Type'='application\json'
    'Authorization'=&quot;Bearer $token&quot;
    }
    $azureContext = Get-AzureRmContext
    $subscriptionId = $azureContext.Subscription.SubscriptionId

    $result = Invoke-RestMethod -Uri &quot;https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/locations/$location/usages?api-version=2017-03-01&quot; -Method Get -Headers $authHeader 
    return $result.value
}


# Get VM quotas
foreach ($location in $locations)
{
    $vmQuotas = Get-AzureRmVMUsage -Location $location
    foreach($vmQuota in $vmQuotas)
    {
        $usage = 0
        if ($vmQuota.Limit -gt 0) { $usage = $vmQuota.CurrentValue / $vmQuota.Limit }
        $json += @&quot;
{ &quot;Name&quot;:&quot;$($vmQuota.Name.LocalizedValue)&quot;, &quot;Category&quot;:&quot;Compute&quot;, &quot;Location&quot;:&quot;$location&quot;, &quot;CurrentValue&quot;:$($vmQuota.CurrentValue), &quot;Limit&quot;:$($vmQuota.Limit),&quot;Usage&quot;:$usage },
&quot;@
    }
}

# Get Network Quota
foreach ($location in $locations)
{
    $networkQuotas = Get-AzureRmNetworkUsage -location $location
    foreach ($networkQuota in $networkQuotas)
    {
        $usage = 0
        if ($networkQuota.limit -gt 0) { $usage = $networkQuota.currentValue / $networkQuota.limit }
         $json += @&quot;
{ &quot;Name&quot;:&quot;$($networkQuota.name.localizedValue)&quot;, &quot;Category&quot;:&quot;Network&quot;, &quot;Location&quot;:&quot;$location&quot;, &quot;CurrentValue&quot;:$($networkQuota.currentValue), &quot;Limit&quot;:$($networkQuota.limit),&quot;Usage&quot;:$usage },
&quot;@
    }

}

# Get Storage Quota
$storageQuota = Get-AzureRmStorageUsage
$usage = 0
if ($storageQuota.Limit -gt 0) { $usage = $storageQuota.CurrentValue / $storageQuota.Limit }
$json += @&quot;
{ &quot;Name&quot;:&quot;$($storageQuota.LocalizedName)&quot;, &quot;Location&quot;:&quot;&quot;, &quot;Category&quot;:&quot;Storage&quot;, &quot;CurrentValue&quot;:$($storageQuota.CurrentValue), &quot;Limit&quot;:$($storageQuota.Limit),&quot;Usage&quot;:$usage }
&quot;@

# Wrap in an array
$json = &quot;[$json]&quot;

# Create the function to create the authorization signature
Function Build-Signature ($omsWorkspaceId, $omsSharedKey, $date, $contentLength, $method, $contentType, $resource)
{
    $xHeaders = &quot;x-ms-date:&quot; + $date
    $stringToHash = $method + &quot;`n&quot; + $contentLength + &quot;`n&quot; + $contentType + &quot;`n&quot; + $xHeaders + &quot;`n&quot; + $resource

    $bytesToHash = [Text.Encoding]::UTF8.GetBytes($stringToHash)
    $keyBytes = [Convert]::FromBase64String($omsSharedKey)

    $sha256 = New-Object System.Security.Cryptography.HMACSHA256
    $sha256.Key = $keyBytes
    $calculatedHash = $sha256.ComputeHash($bytesToHash)
    $encodedHash = [Convert]::ToBase64String($calculatedHash)
    $authorization = 'SharedKey {0}:{1}' -f $omsWorkspaceId,$encodedHash
    return $authorization
}


# Create the function to create and post the request
Function Post-OMSData($omsWorkspaceId, $omsSharedKey, $body, $logType)
{
    $method = &quot;POST&quot;
    $contentType = &quot;application/json&quot;
    $resource = &quot;/api/logs&quot;
    $rfc1123date = [DateTime]::UtcNow.ToString(&quot;r&quot;)
    $contentLength = $body.Length
    $signature = Build-Signature `
        -omsWorkspaceId $omsWorkspaceId `
        -omsSharedKey $omsSharedKey `
        -date $rfc1123date `
        -contentLength $contentLength `
        -fileName $fileName `
        -method $method `
        -contentType $contentType `
        -resource $resource
    $uri = &quot;https://&quot; + $omsWorkspaceId + &quot;.ods.opinsights.azure.com&quot; + $resource + &quot;?api-version=2016-04-01&quot;

    $headers = @{
        &quot;Authorization&quot; = $signature;
        &quot;Log-Type&quot; = $logType;
        &quot;x-ms-date&quot; = $rfc1123date;
    }

    $response = Invoke-WebRequest -Uri $uri -Method $method -ContentType $contentType -Headers $headers -Body $body -UseBasicParsing
    return $response.StatusCode

}

# Submit the data to the API endpoint
Post-OMSData -omsWorkspaceId $omsWorkspaceId -omsSharedKey $omsSharedKey -body ([System.Text.Encoding]::UTF8.GetBytes($json)) -logType $logType
[/powershell]

When you configure the script to run, make sure you populate your OMS Workspace ID and Shared Key (which you can get by logging into your OMS portal and then going to Settings &gt; Connected Sources) and the list of Azure locations that you're interested in (formatted as a JSON array, e.g. ["australiaeast", "australiasoutheast"] ).

Once you have a few points of data in OMS, you can use the Log Analytics search and alerting capabilities to query, visualise or alert on the Azure quota data. Note that as per the Log Collector API documentation, OMS will suffix both your event type and fields. So your events will have a type of <strong>AzureQuota_CL </strong>and the fields will be suffixed with _s (for strings) and _d for numbers. The following query will give you your raw events:

<strong>* Type=AzureQuota_CL</strong>

<a href="https://msdnshared.blob.core.windows.net/media/2017/06/QuotaList.png"><img width="237" height="300" class="alignnone size-medium wp-image-2665" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/QuotaList-237x300.png" /></a>

Then you can do some fancier queries to get more insights. For example, to graph your % utilisation in a specific region:

<strong>* Type=AzureQuota_CL Location_s=australiaeast  | measure avg(Usage_d) by Name_s interval 1MINUTE</strong>

But, remember our real goal was to get notified when your usage starts getting close you your limit. You can find any instances above a threshold (say, 80%) with the following query:

<strong>* Type=AzureQuota_CL  Usage_d &gt; 0.80</strong>

With this, you can use the OMS Alert capabilities to send an email, invoke a WebHook, call a runbook or call an ITSM system.

<a href="https://msdnshared.blob.core.windows.net/media/2017/06/QuotaAlert.png"><img width="1024" height="548" class="alignnone wp-image-2675 size-large" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/QuotaAlert-1024x548.png" /></a>

So with a few lines of PowerShell and Azure Automation used as glue between the Azure APIs and OMS Log Analytics, you'll never be surprised by hitting a quota again. Happy alerting!]]></content:encoded>
    </item>
    <item>
      <title>Industrial Scale Onboarding in Microsoft Azure</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/industrial-scale-onboarding-in-microsoft-azure</link>
      <pubDate>Tue, 30 May 2017 11:37:42 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/?p=2625</guid>
      <description><![CDATA[In my last post on Industrial Scale RBAC, I alluded to the fact that in an enterprise context you'll...]]></description>
      <content:encoded><![CDATA[In my last post on <a href="https://blogs.msdn.microsoft.com/tomholl/2017/05/29/industrial-scale-rbac-in-microsoft-azure/">Industrial Scale RBAC</a>, I alluded to the fact that in an enterprise context you'll likely need some special processes and tools for onboarding new projects into Azure. In this post, I'm going to break this down. As with my last post, the advice in this one isn't likely to be applicable in small organisations or where a team has a subscription to themselves. But in a large enterprise with many teams working on different projects while sharing subscriptions, things start to get interesting.

To see why, let's pay a visit to everyone's favourite multinational, Contoso. After following the guidance in the <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-subscription-governance">Azure Enterprise Scaffold</a>, Contoso's central cloud team has already set up their foundational Azure environment, by setting up subscriptions for production and non-production work, VNETs and subnets, connectivity to their on-premises networks, defining naming standards and writing polices on what can be deployed in which regions. They also have defined a chargeback regime, whereby each cost centre needs to pay for the resources used by their projects.

What they don't have yet is any actual workloads running in Azure. This is not due to lack of demand—in fact there are a whole bunch of teams itching to deploy resources to the cloud. But the cloud team is fearful that once they start granting access to the Azure subscription it will become a free-for-all, where loads of resources are deployed with little regard to their careful planning, and no way of knowing which resources are owned by which team and used for what purpose.

What we need here is some governance! Contoso's central cloud team needs to know when new projects are being established, ensure that the right people have access to the resources, and have a way of tracking which resources are owned by which team and cost centre for chargeback purposes. They also need to be able to enforce additional rules such as restricting resources to certain locations or preventing people from creating things that should be centrally managed (like VNET gateways or Key Vaults). The Contoso project teams probably don't care much about any of these things, but they know that unless the central cloud team is happy they aren't going to get access to the platform.

The solution here is to define a process that uses <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview#resource-groups">Azure Resource Groups</a>, <a href="https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-what-is">RBAC</a> and <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-policy">Resource Policy</a> to onboard new projects in a predictable and compliant manner. At the centre of the solution is the idea that Resource Groups are created and configured centrally, after which project teams are free to deploy what they want, subject to the constraints baked into the resource group.

How you implement this process will depend on your organisation's maturity, toolsets and the frequency which new projects are onboarded. If you only get a new project every few months, you could do all of this manually using the portal. If projects are springing up randomly and frequently, you could implement a self-service request portal backed by a workflow with approvals and automation. In this post I've put together a starter PowerShell script that demonstrates the key steps, but note that this is not production quality and you'll need to think about what is appropriate in your onboarding process.

My starter process does the following:
<ol>
 	<li>The script is invoked, specifying the project name and additional metadata (cost centre, deployment location, etc).</li>
 	<li>Two AAD groups are created, one for team contributors and one for team readers. Note the script doesn't add users to these groups, so you'll need to do that separately.</li>
 	<li>A new resource group is created in accordance with a simple naming convention</li>
 	<li>Tags are applied to the resource group, recording the project name, cost centre, etc.</li>
 	<li>RBAC is applied to the resource group, assigning the Contributor and Reader role to each of the two new AAD groups</li>
 	<li>Resource Policy is applied to the resource group. I've got two policies, but you could add more:
<ol>
 	<li>A policy that applies the resource group's Tags to each resource that is added to the group</li>
 	<li>A policy that prevents resources from being deployed outside of a list of approved locations</li>
</ol>
</li>
</ol>
The code for my starter process is below. Note that the PowerShell script requires that you have a custom policy definition called <strong>taggingPolicy</strong> already created. The XML and PowerShell to create this is in the following section.

[caption id="attachment_2635" align="alignright" width="300"]<a href="https://msdnshared.blob.core.windows.net/media/2017/05/Tags.png"><img width="300" height="129" class="wp-image-2635 size-medium" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/05/Tags-300x129.png" /></a> Azure Policy allows you to automatically set the correct tags on resources created in the resource group.[/caption]
<h2>Onboarding PowerShell Script</h2>

[powershell]param(
    [string]$projectName,
    [string]$projectContact,
    [string]$costCentre,
    [string]$environment,
    [string]$subscriptionId,
    [array]$locations # First location is where the RG will be created.
)

# TODO: Validate the inputs

# Log in
Login-AzureRmAccount
$sub = Select-AzureRmSubscription -SubscriptionId $subscriptionId
Connect-AzureAD -TenantId $sub.Tenant.TenantId

# Create AAD groups
# TODO: Check if they already exist 
$contributorsGroupName = &quot;$projectName-$environment-Contributors&quot;
$readersGroupName = &quot;$projectName-$environment-Readers&quot;
New-AzureADGroup -DisplayName $contributorsGroupName -MailEnabled $false -SecurityEnabled $true -MailNickName $contributorsGroupName
New-AzureADGroup -DisplayName $readersGroupName -MailEnabled $false -SecurityEnabled $true -MailNickName $readersGroupName

# Create Azure Resource Group
# TODO: Check if it already exists
$rgName = &quot;$projectName-$environment-RG&quot; # or whatever your naming convention is
$tags = @{&quot;ProjectName&quot;=$projectName; ProjectContact=$projectContact; CostCentre=$costCentre; Environment=$environment}
$rg = New-AzureRmResourceGroup -Name $rgName -Location $locations[0] -Tag $tags

# Set RBAC for the Resource Group
$contributorsGroup = Get-AzureRmADGroup -SearchString $contributorsGroupName 
$readersGroup = Get-AzureRmADGroup -SearchString $readersGroupName
New-AzureRmRoleAssignment -ObjectId $contributorsGroup.Id -RoleDefinitionName &quot;Contributor&quot; -Scope $rg.ResourceId
New-AzureRmRoleAssignment -ObjectId $readersGroup.Id -RoleDefinitionName &quot;Reader&quot; -Scope $rg.ResourceId

# Assign policies
$tagPolicy = Get-AzureRmPolicyDefinition -Name taggingPolicy
New-AzureRMPolicyAssignment -Name &quot;$rgname-TagPolicy&quot; -Scope $rg.ResourceId -PolicyDefinition $tagPolicy -PolicyParameterObject $tags # Assumes policy already created with this name
$locationPolicy =  Get-AzureRmPolicyDefinition | where-object { $_.Name -eq &quot;e56962a6-4747-49cd-b67b-bf8b01975c4c&quot; } # Built-in location policy
New-AzureRMPolicyAssignment -Name &quot;$rgname-LocationPolicy&quot; -Scope $rg.ResourceId -PolicyDefinition $locationPolicy -PolicyParameterObject @{listOfAllowedLocations=$locations}[/powershell]

<h2>Tagging Policy</h2>
<h3>Policy file</h3>

[xml]{
    &quot;if&quot;: {
        &quot;field&quot;: &quot;tags&quot;,
        &quot;exists&quot;: &quot;false&quot;
    },
    &quot;then&quot;: {
        &quot;effect&quot;: &quot;append&quot;,
        &quot;details&quot;: [
            {
                &quot;field&quot;: &quot;tags&quot;,
                &quot;value&quot;: {
                    &quot;ProjectName&quot;: &quot;[parameters('projectName')]&quot;,
                    &quot;ProjectContact&quot;: &quot;[parameters('projectContact')]&quot;,
                    &quot;CostCentre&quot;: &quot;[parameters('costCentre')]&quot;,
                    &quot;Environment&quot;: &quot;[parameters('environment')]&quot;
                }
            }
        ]
    }
}
[/xml]

<h3>Parameters file</h3>

[xml]{
    &quot;projectName&quot;: {
        &quot;type&quot;: &quot;string&quot;,
        &quot;metadata&quot;: {
            &quot;description&quot;: &quot;The short name of the project&quot;,
            &quot;displayName&quot;: &quot;Project Name&quot;
        }
    },
    &quot;projectContact&quot;: {
        &quot;type&quot;: &quot;string&quot;,
        &quot;metadata&quot;: {
            &quot;description&quot;: &quot;The username of the main contact for this project&quot;,
            &quot;displayName&quot;: &quot;Project Contact&quot;
        }
    },
    &quot;costCentre&quot;: {
        &quot;type&quot;: &quot;string&quot;,
        &quot;metadata&quot;: {
            &quot;description&quot;: &quot;The cost centre that usage will be charged to&quot;,
            &quot;displayName&quot;: &quot;Cost Centre&quot;
        }
    },
    &quot;environment&quot;: {
        &quot;type&quot;: &quot;string&quot;,
        &quot;metadata&quot;: {
            &quot;description&quot;: &quot;The code for the environment for this deployment&quot;,
            &quot;displayName&quot;: &quot;Environment&quot;
        }
    }
}
[/xml]

<h3>PowerShell to create the Policy Definition</h3>

[powershell]$policy = New-AzureRmPolicyDefinition -Name taggingPolicy -Description &quot;Policy to append default tags to all resources&quot; -Policy &quot;.\TagPolicy.json&quot; -Parameter &quot;.\TagPolicyParameters.json&quot;[/powershell]
]]></content:encoded>
    </item>
    <item>
      <title>Industrial Scale RBAC in Microsoft Azure</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/industrial-scale-rbac-in-microsoft-azure</link>
      <pubDate>Mon, 29 May 2017 12:01:52 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/?p=2605</guid>
      <description><![CDATA[If you're building a cloud service with just a few of your closest friends, you can forgiven for not...]]></description>
      <content:encoded><![CDATA[If you're building a cloud service with just a few of your closest friends, you can forgiven for not thinking about securing your resources—allowing everyone in the team to do anything to all resources in the subscription may be a fine strategy. But if your team is a part of a big company, with multiple teams sharing the same subscriptions, it's another story. Ensuring that people outside your team can't modify, or in some cases see your resources may be critical to the success of your project.

Fortunately, Azure's <a href="https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-what-is">Role-Based Access Control</a> (RBAC) solves this problem by allowing you to assign fine-grained permissions that dictate who can do what which resources. However it isn't always obvious how to use the power of Azure RBAC to achieve the best result in a typical enterprise development scenario. But fear not, I'm here to help!

In a nutshell, Azure RBAC is about putting people (users or groups) into roles (built-in or custom) at a defined scope (subscription, resource group or resource). There are an awful lot of possible permutations here, but in practice only a few of them make sense.

The following diagram shows a canonical enterprise Azure RBAC model within a subscription. The roles (well, some of them) are in the columns and the scopes (at subscription and resource group level) are in the rows. The assignments are in the intersections—the group icons show just an example of what's possible.

<img width="1309" height="646" class="alignnone size-full wp-image-2615" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/05/AzureRBAC.png" />

First, let's look a the golden guy. He or she has full access to everything in the subscription, the keys to the castle. This role assignment applies automatically to the subscription owner, which will ideally be a service account. You can put other people in this role, but in an enterprise scenario you won't want to give mere mortals such power and responsibility. However one key thing that this role can do is both create new resource groups, and assign permissions on those groups. You'll see why this is important shortly.

Now, let's look at the other subscription-level assignments. In this model, normal members of a development team are not granted any subscription-level rights, in particular we don't grant them Contributor, which would allow them to create new resource groups. Instead we reserve subscription-level role assignments for these situations:
<ul>
 	<li>Subscription Reader role, for people who have a need to view (but not change) any resources deployed in the subscription</li>
 	<li>Subscription "specialist" roles, for people who deal with specific technologies across any resource group. For example a network engineer may be granted "Network Contributors" so they can view and modify network resources (but nothing else) in any resource group.</li>
</ul>
Project team members are assigned permissions at the resource group scope. In most cases, you would assign them Contributor access which will allow them to do everything to any resources within that resource group, including creating and deleting resources. The only difference between Owner and Contributor is that Owners can also set RBAC permissions. We don't want to allow this in this model as in an enterprise situation you typically want to control access centrally rather than delegate it to individual teams.

You may also want to assign people to less-privileged role within an individual resource group, for example Reader or one of the specialist roles if they are working on a specific project but don't need full access to all resources.

This whole model is predicated on resource groups being "pre-created" for a project team to work in, with appropriate permissions set up by a central process, team and/or tools. Again, this model isn't going to be appropriate for small organisations or perhaps for development or sandpit subscriptions. But in a large enterprise environment, this kind of governance is usually required. In my <a href="https://blogs.msdn.microsoft.com/tomholl/2017/05/30/industrial-scale-onboarding-in-microsoft-azure/">next post</a> I'll describe what this may look like in more detail.

Finally, a few more tips on effective industrial-scale RBAC:
<ul>
 	<li><em>Avoid role assignments at the resource level<strong> </strong></em>(for example, on an individual Virtual Machine). While there may be valid scenarios for this, setting permission at such a granular level is likely to be hard to manage and cause confusion.</li>
 	<li><em>Use built-in roles where possible. </em><a href="https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-custom-roles">Custom roles</a> are a powerful and sometimes useful capability, but they should be reserved for cases when you really need them as they can also introduce complexity. There are 38 <a href="https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles">built-in roles </a>which should cover most normal scenarios.</li>
 	<li><em>Assign access to groups, not users. </em>The Azure Portal is not really the ideal place to define who is working in which teams. A better approach is to put the users into a Group (in AAD directly, or in AD synced to AAD) and then grant Azure permissions to the group. This way you can add and remove group members externally to Azure, with permissions always being current, while also allowing that group to be used for other purposes such as mailing lists.</li>
 	<li><i>Don't use Microsoft Accounts. </i>Azure access can be assigned to users in your Azure Active Directory, or to personal Microsoft Accounts. In an enterprise scenario, Microsoft Accounts are normally a bad idea, as they aren't associated with an organisation who can do things like disable their account when they leave the company. If you're collaborating with people from outside your organisation, <a href="https://docs.microsoft.com/en-us/azure/active-directory/active-directory-b2b-what-is-azure-ad-b2b">AAD B2B Collaboration</a> can be used to invite those external users as "guests" in your directory.</li>
 	<li><em>Don't forget Service Accounts! </em>In a mature team, most deployments and resource changes should be made by automated processes such as CI/CD or Azure Automation scripts, rather than by individual users. If this applies to you, make sure your RBAC supports this by limiting rights to deploy resources to service accounts instead of users.</li>
</ul>
The model and tips described in this article have been derived from the requirements and solutions I've seen from a number of large organisations adopting Azure, but every team and every company are different, and you should feel free to use the available tools in whatever way works best for you. If you've got some experiences to share, I'd love to hear them in the comments.]]></content:encoded>
    </item>
    <item>
      <title>Exporting details of Azure Virtual Machines to CSV/Excel</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/dumping-details-of-azure-virtual-machines-to-csvexcel</link>
      <pubDate>Tue, 22 Nov 2016 05:57:14 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/?p=2595</guid>
      <description><![CDATA[In my current role as a Cloud Solution Architect, I'm often asked to take a look at a customer's...]]></description>
      <content:encoded><![CDATA[In my current role as a Cloud Solution Architect, I'm often asked to take a look at a customer's existing Azure environments and provide advice on how they can be improved. The first step is always to get an understanding of what it is that they've deployed. The Azure Portal provides a great exploration experience, but it's no good for more detailed analysis such as determining how many VMs there are of each size, how many are in availability sets, etc.

Fortunately you can get all of this information in PowerShell. However the commands are different for ARM and Classic VMs, and to pull all of the relevant information can take a few separate commands. After doing this manually for a few customers, I've written some reusable scripts that you can run against any Azure subscription. I've built two scripts, one for ARM and one for Classic, each of which runs against all of the subscriptions you have access to (or if you choose, just those tied to a specific Azure Active Directory tenant) and writes the info about each VM into a CSV file, ready to be loaded into Excel.

The scripts currently pull out the following info, but it should be easy to tweak if you need something different:
<ul>
 	<li>Subscription Name</li>
 	<li>Deployment model (ARM or Classic)</li>
 	<li>VM Name</li>
 	<li>Resource Group Name (ARM) or Cloud Service Name (Classic)</li>
 	<li>Location</li>
 	<li>VM Size</li>
 	<li>Status</li>
 	<li>Availability Set</li>
</ul>
Check out the scripts at the TechNet Script Center:
<ul>
 	<li><a href="https://gallery.technet.microsoft.com/scriptcenter/Dump-Azure-ARM-VMs-to-CSV-e6f52a7b">Dump Azure ARM VMs to CSV</a></li>
 	<li><a href="https://gallery.technet.microsoft.com/scriptcenter/Dump-Azure-Classic-VMs-to-d964d3ce">Dump Azure Classic VMs to CSV</a></li>
</ul>
&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Migrating Azure IaaS solutions from ASM to ARM using MigAz</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/migrating-azure-iaas-solutions-from-asm-to-arm-using-migaz</link>
      <pubDate>Sat, 16 Jul 2016 07:51:45 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/?p=2575</guid>
      <description><![CDATA[Anyone who has previously deployed Azure IaaS solutions using the older Azure Service Management...]]></description>
      <content:encoded><![CDATA[Anyone who has previously deployed Azure IaaS solutions using the older Azure Service Management (ASM) model should be thinking about how and when to migrate it to <a href="https://azure.microsoft.com/en-us/documentation/articles/resource-group-overview/">Azure Resouce Manager</a> (ARM). While ASM will continue to be supported, ARM provides a number of benefits such as template-based deployment, Role-Based Access Control and Tagging. There are a number of tools that can help with this migration, and today I'm going to describe a new tool called <a href="https://aka.ms/migaz">MigAz</a>.

<em><strong>Note</strong></em><em>: Azure’s capabilities and APIs change rapidly. This information is current as of July 2016. If you’re reading this in the distant future, you may want to check with other sources on current capabilities and best practices.</em>
<h2>ASM to ARM Migration Options</h2>
At the time of writing, there are three main approaches you can use to migrate Azure IaaS resources (i.e. Compute, Networking and Storage) from ASM to ARM:
<ol>
	<li><a href="https://github.com/fullscale180/asm2arm">ASM2ARM</a></li>
	<li><a href="https://azure.microsoft.com/en-us/blog/iaas-migration-classic-resource-manager/">Platform Supported Migration</a></li>
	<li><a href="https://aka.ms/migaz">MigAz</a></li>
</ol>
Each of these tools has their place, and it's great to have a few options available. ASM2ARM is PowerShell based, and I've seen a few cases where people have used it as the basis for their own custom migration solutions to meet unique requirements. The Platform Supported Migration is cool because it actually converts running resources from ASM to ARM, rather than creating new ARM resources that are copies of the original. However this tool requires a one-way "big bang" migration per VNET and doesn't give you a chance to clean things up on the way, so it's not ideal in all situations.

MigAz is the new kid on the block, and I like it because it provides a great balance between simplicity and flexibility.
<h2>What is MigAz?</h2>
MigAz is a tool created by Paulo Ramos that is based on a simple but powerful idea. Rather than migrate your ASM resources, MigAz builds a shiny new <a href="https://azure.microsoft.com/en-us/documentation/articles/resource-group-authoring-templates/">ARM Template</a> that represents the ARM equivalent for your chosen ASM resources. This is cool a few reasons.

First, you can select whatever combination of ASM resources you want, and since they get represented in a single ARM Template that means they will ultimately become a single ARM Resource Group. This gives you an opportunity to create meaningful Resources Groups as you migrate, which will simplify management and deployment in the future. You can also leave out resources that you no longer want.

Second, since the output is just a JSON file, you're free to customise it as little or much as you want before you deploy. Don't like the name of your resource? Change it. Want to change the way your Cloud Services are represented with ARM concepts like Load Balancers and Availability Sets? Change it. And because ARM Templates can be redeployed over existing Resource Groups, you can improve things iteratively.

Finally, since MigAz creates an ARM template, you've got an artefact that you can check in to source control to start your "configuration as code" DevOps journey. This means you'll be able to deploy your solution to multiple environments without manual effort or risk of inconsistencies or errors. (To get the most out of this approach, you'll want to evolve your template to include environment-specific Parameters files).
<h2>Using MigAz</h2>
To install and learn how to use MigAz, go to <a href="https://aka.ms/migaz">https://aka.ms/migaz</a> where you'll find the latest release and documentation. I won't try to replicate the full set of instructions, but the basic migration approach is as follows:
<ol>
	<li>Run MigAz.exe on a Windows PC or server.</li>
	<li>Sign into your Azure account.</li>
	<li>Select the ASM resources that you want to export.</li>
	<li>Export the resources to an ARM template.</li>
	<li>Use Azure PowerShell to deploy the ARM template to a new Resource Group. If the template includes Virtual Machines, you'll get errors the first time you deploy. Don't worry, this is normal as you haven't yet copied the VHDs from the ASM storage account to the new ARM one.</li>
	<li>Run the BlobCopy.ps1 PowerShell script included with MigAz to copy the VHDs into the new storage account.</li>
	<li>Redeploy the ARM template to the same Resource Group. With the VHDs in place, everything should be ready to go.</li>
	<li>Test the ARM deployment and edit/redeploy the template as needed.</li>
	<li>Decommission the ASM resources at the appropriate time.</li>
</ol>
<h2>Contributing to MigAz</h2>
Is there something you don't like about the tool? Found a bug? Well, MigAz is a community-supported open source project on GitHub, so you can submit issues, fork the repository and even update the code and submit pull requests. Come on, give it a go! (<a href="https://github.com/Azure/classic-iaas-resourcemanager-migration/commit/1606a808435a6393b22efba11309ca855259ff3e">I did</a> :-).]]></content:encoded>
    </item>
    <item>
      <title>Analysing Enterprise Azure spend by Tags</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/analysing-enterprise-azure-spend-by-tags</link>
      <pubDate>Tue, 08 Mar 2016 18:28:00 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/2016/03/08/analysing-enterprise-azure-spend-by-tags/</guid>
      <description><![CDATA[Azure Resource Manager allows you to assign custom "tags" to resources such as VMs or storage...]]></description>
      <content:encoded><![CDATA[Azure Resource Manager allows you to assign custom "tags" to resources such as VMs or storage accounts. This allows you to put custom metadata onto your resources, such as which application it belongs to, which department owns it and which environment it is a part of. While the tag information is available through Azure's billing APIs, the format of this information makes it tricky to analyse your overall Azure spend by tag values. This post shows one way to put the tag data into a structured format that will allow you to easily analyse billing data by tags using Excel or Power BI.

<strong><em>Note</em></strong><em>: Azure’s capabilities and APIs change rapidly. This information is current as of March 2016. If you’re reading this in the distant future, you may want to check with other sources on current capabilities and best practices.</em>
<h2>Scenarios</h2>
If you've deployed just one or two solutions to the cloud, your spend is probably pretty predictable - it may go up or down over time, but you should be able to correlate this with things like usage of your application and scaling up of resources. However once you have a lot of different teams deploying to the cloud, each with different applications in different stages of their lifecycle and multiple environments, it can be very difficult to understand what's deployed and what it is all costing.

To help you find some order in this chaos, any Azure resources deployed using the Azure Resource Manager model can be annotated with Tags. Tags are just name-value pairs of text strings and can be used to convey whatever you want - but the most obvious uses are to categorise resources according to some of the dimensions I listed above, such as application name, owning team/department or environment. Because you can apply multiple tags to a single resource, you can categorise your resources in multiple ways - for example you could view all the resources that make up Project Vulcan, all resources owned by Q-Branch or all resources deployed to the Staging environment.

Of course, tagging is only useful if the tags are applied consistently to your resources. To facilitate this, you can define <a href="https://azure.microsoft.com/en-us/documentation/articles/resource-manager-policy/">Policies</a> that will require all resoruces to be tagged in accordance with your chosen taxonomy, or the deployment will fail.
<h2>Accessing Azure Billing Data</h2>
To enable people to analyse their Azure usage costs, the billing data is exposed through a couple of different APIs. If you have a Pay-as-you-go subscription you can use the <a href="https://msdn.microsoft.com/en-us/library/azure/mt218998.aspx">Resource Usage and Resource Ratecard APIs</a>. However organisations with a large number of Azure resources deployed are more likely to have Enterprise Agreement (EA) subscriptions, in which case you can use the <a href="https://redbaron.blob.core.windows.net/public/BillingandUsageAPI_20150205.pdf">EA Billing and Usage API</a> to get this data. The sample in this post uses the EA approach.

Once you have the data downloaded, you can analyse it using tools like Excel or Power BI. In fact if you're using Power BI you can avoid directly accessing the API altogether, as the <a href="https://powerbi.microsoft.com/en-us/documentation/powerbi-content-pack-azure-enterprise/">Microsoft Azure Enterprise content pack for Power BI</a> can automatically connect to the API, download your data and show it on a pretty interactive dashboard.

So, problem solved, right? Alas no... while the data returned from the APIs does include the resource tags, the complete set of tags assigned to each resource are trapped within a single field in JSON format, as shown below. So while we technically have all the data required to analyse usage and cost by tag, it's not in a format that tools like Excel or Power BI can make use of.

<img src="https://acom.azurecomcdn.net/80C57D/cdn/mediahandler/docarticles/dpsmedia-prod/azure.microsoft.com/en-us/documentation/articles/resource-group-using-tags/20160308123832/billing_csv.png" alt="See tags in billing" />
<h2>Making this Work</h2>
To allow for meaningful analysis by tag, we need to take that single blob of JSON in the Tags field and split it into its component elements. To do this, I started with a <a href="https://www.redbaronofazure.com/?p=631">script that Christer Ljung put together</a> which uses the EA Usage API to download usage data and save to CSV. I then fixed a few things to cater for changes in the API since he wrote his script and added some additional logic to split the tags into individual columns. The complete PowerShell is below; to call it copy it to a .ps1 file and call it with <strong>.\Download_BillingData.ps1 &lt;EnrollmentNbr&gt; &lt;Key&gt; &lt;Month&gt;</strong> where you can get your enrollment number and key from <a href="https://ea.azure.com">https://ea.azure.com</a> and &lt;<strong>Month&gt; </strong>is formatted like 2016-03.
<pre>Param(
   [string]$EnrollmentNbr = "",
   [string]$Key = "",
   [string]$Month = ""
)
# access token is "bearer " and the the long string of garbage
$AccessToken = "Bearer $Key"
$urlbase = 'https://ea.azure.com'
$csvAll = @()

Write-Verbose "$(Get-Date -format 's'): Azure Enrollment $EnrollmentNbr"

# function to invoke the api, download the data, import it, and merge it to the global array
Function DownloadUsageReport( [string]$LinkToDownloadDetailReport, $csvAll )
{
		Write-Verbose "$(Get-Date -format 's'): $urlbase/$LinkToDownloadDetailReport)"
		$webClient = New-Object System.Net.WebClient
		$webClient.Headers.add('api-version','1.0')
		$webClient.Headers.add('Authorization', "$AccessToken")
		$data = $webClient.DownloadString("$urlbase/$LinkToDownloadDetailReport")
		# remove the funky stuff in the leading rows - skip to the first header column value
		$pos = $data.IndexOf("AccountOwnerId")
		$data = $data.Substring($pos-1)
		# convert from CSV into an ps variable
		$csvM = ($data | ConvertFrom-CSV)
		# merge with previous
		$csvAll = $csvAll + $csvM
		Write-Verbose "Rows = $($csvM.length)"
		return $csvAll
}

if ( $Month -eq "" )
{
	# if no month specified, invoke the API to get all available months
	Write-Verbose "$(Get-Date -format 's'): Downloading available months list"
	$webClient = New-Object System.Net.WebClient
	$webClient.Headers.add('api-version','1.0')
	$webClient.Headers.add('Authorization', "$AccessToken")
	$months = ($webClient.DownloadString("$urlbase/rest/$EnrollmentNbr/usage-reports") | ConvertFrom-Json)

	# loop through the available months and download data. 
	# List is sorted in most recent month first, so start at end to get oldest month first 
	# and avoid sorting in Excel
	for ($i=$months.AvailableMonths.length-1; $i -ge 0; $i--) {
		$csvAll = DownloadUsageReport $($months.AvailableMonths.LinkToDownloadDetailReport[$i]) $csvAll
	}
}
else
{
	# Month was specified as a parameter, so go ahead and just download that month
	$csvAll = DownloadUsageReport "rest/$EnrollmentNbr/usage-report?month=$Month&amp;type=detail" $csvAll
}
Write-Host "Total Rows = $($csvAll.length)"

# data is in US format wrt Date (MM/DD/YYYY) and decimal values (3.14)
# so loop through and convert columns to local format so that Excel can be happy
Write-verbose "$(Get-Date -format 's'): Fixing datatypes..."
for ($i=0; $i -lt $csvAll.length; $i++) {
	$csvAll[$i].Date = [datetime]::ParseExact( $csvAll[$i].Date, 'dd/mm/yyyy', $null).ToString("d")
	$csvAll[$i].ExtendedCost = [float]$csvAll[$i].ExtendedCost
	$csvAll[$i].ResourceRate = [float]$csvAll[$i].ResourceRate
	$csvAll[$i].'Consumed Quantity' = [float]$csvAll[$i].'Consumed Quantity'

    # Expand tags
    $tags = $csvAll[$i].Tags | ConvertFrom-Json
    if ($tags -ne $null) {
         $tags.psobject.properties | ForEach { 
            $tagName = "Tag-$($_.Name)" 
            Add-Member -InputObject $csvAll[$i] $tagName $_.Value 
            # Add to first row, as that's what is used to format the CSV
            if ($csvAll[0].psobject.Properties[$tagName] -eq $null) {
                Add-Member -InputObject $csvAll[0] $tagName $null -Force
            }
        }
    }

}

# save the data to a CSV file
$filename = ".\$($EnrollmentNbr)_UsageDetail$($Month)_$(Get-Date -format 'yyyyMMdd').csv"
Write-Host "$(Get-Date -format 's'): Saving to file $filename"
$csvAll | Export-Csv $filename -NoTypeInformation -Delimiter ","</pre>
<h2>The Result</h2>
After running the script, you should have a nice neat CSV file with your Azure usage data for the chosen month.

If you open this file in Excel, you should see that, in addition to the original JSON-formatted Tags column, you'll have a new column called <strong>Tag-something</strong> for each tag that was found for at least one Azure resource.

<a href="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-43-90/7343.Excel.png"><img src="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-43-90/7343.Excel.png" alt="" border="0" /></a>

While there's unfortunately no way to get the Enterprise Azure Content Pack for Power BI to do the same, you can load the same CSV file into Power BI and create your own dashboards. You should see the same <strong>Tag-something</strong> columns available to put into your charts.

<a href="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-43-90/6763.PowerBI.PNG"><img src="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-43-90/6763.PowerBI.PNG" alt="" border="0" /></a>

Happy analysing!]]></content:encoded>
    </item>
    <item>
      <title>Authenticating to Azure Resource Manager using AAD and Certificates</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/authenticating-to-azure-resource-manager-using-aad-and-certificates</link>
      <pubDate>Thu, 18 Jun 2015 21:22:30 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/2015/06/18/authenticating-to-azure-resource-manager-using-aad-and-certificates/</guid>
      <description><![CDATA[In a previous post I described how to perform unattended authentication to Azure Active Directory...]]></description>
      <content:encoded><![CDATA[<p>In a <a href="https://blogs.msdn.com/b/tomholl/archive/2014/11/25/unattended-authentication-to-azure-management-apis-with-azure-active-directory.aspx">previous post</a> I described how to perform unattended authentication to Azure Active Directory using a username and password. This post takes this further by showing how to use certificates, rather than passwords, to authenticate. Using certificates for authentication is generally considered more secure, as there are no credentials in your code and it cannot run without an appropriate certificate installed on the machine executing the script. This approach can be used for anything that authenticates using AAD, but I’ll describe specially how to use this to call the Azure Resource Manager APIs from .NET, allowing your code to interrogate and/or manipulate Azure resources.</p>  <p>This post is basically combining ideas and code from the <strong><a href="https://github.com/AzureADSamples/Daemon-CertificateCredential-DotNet">Daemon-CertificateCredential-DotNet</a></strong> AAD sample and Neil Mackenzie’s post on <a href="https://blogs.msdn.com/b/cloud_solution_architect/archive/2015/05/14/using-a-service-principal-for-azure-powershell-authentication.aspx">Using a Service Principal for Azure PowerShell Authentication</a>. </p>  <p><em><strong>Note</strong>: Azure’s capabilities and APIs change rapidly. This information is current as of June 2015. If you’re reading this in the distant future, you may want to check with other sources on current capabilities and best practices.</em></p>  <h2>Create a new Application in your AAD tenant</h2>  <p>Open up the <a href="https://manage.windowsazure.net">Old Azure Management Portal</a>, browse to the Azure Active Directory tenant associated with your description, click on the Applications tab and then click Add. Enter a nice name for your application and choose “Web Application and/or Web API” as the type. You’ll need to complete the Sign-on URL and the App ID URI but it doesn’t really matter what they are in this case. Once the application has been configured, click on the Configure tab and make a note of the Client ID.</p>  <h2></h2>  <h2></h2>  <h2>Associate a Certificate with your AAD account</h2>  <p>Next you’ll need to create/acquire a certificate to use for authentication. If you haven’t got one handy, you can create a self-signed one using the <strong>makecert</strong> tool which you should have access to from a Visual Studio developer command prompt:</p>  <p><code>makecert -r -pe -n &quot;CN=MyAzureManagement&quot; -ss My -len 2048 c:\temp\MyAzureManagement.cer</code></p>  <p>To associate the certificate with your AAD account you’ll need to install the <a href="https://technet.microsoft.com/en-us/library/jj151815.aspx">Azure AD PowerShell module</a>.</p> <code>connect-msolservice    <br />$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate     <br />$cer.Import(&quot;c:\temp\MyAzureManagement.cer&quot;)     <br />$binCert = $cer.GetRawCertData()     <br />$credValue = [System.Convert]::ToBase64String($binCert);     <br />New-MsolServicePrincipalCredential -AppPrincipalId &quot;&lt;Client ID for your AAD app&gt;&quot; -Type asymmetric -Value $credValue -Usage verify</code>   <h2>Assign your AAD application to an Azure Management Role</h2>  <p>Even though we can now authenticate as your AAD application using a certificate, that account doesn’t yet have permissions to do anything to your Azure account. To enable this, we’ll need to add your AAD application to the appropriate Azure Resource Manager role. Remember that ARM has full role-based access control, so you can be quite fine-grained in what the application should be allowed to do.</p>  <p>The new <a href="https://portal.azure.com/">Azure Portal</a> currently lets you assign permissions to AAD <em>Users</em>, but not to AAD <em>Applications</em>. Since we want to do the latter, we’ll need to use the Azure PowerShell module. In this case I’m adding the application to the “Contributes” role for a specific resource group.</p> <code>Switch-AzureMode AzureResourceManager    <br />New-AzureRoleAssignment `     <br />&#160;&#160; -ServicePrincipalName &quot;&lt;Client ID for your AAD app&gt;&quot; `     <br />&#160;&#160; -RoleDefinitionName &quot;Contributor&quot; `     <br />&#160;&#160; -Scope &quot;/subscriptions/&lt;Your Azure SubscriptionId&gt;/resourceGroups/Default-Web-WestUS&quot;</code>&#160; <h2>Authenticate to AAD and call the Azure Resource Manager APIs</h2>  <p>Finally we have everything we need to call the APIs. As far as I know it’s not possible to use certificates to authenticate to AAD from PowerShell, so we will be doing this from .NET.</p>  <p>The following sample code should run happily in a console app if you add the ADAL (Microsoft.IdentityModel.Clients.ActiveDirectory) and ARM (Microsoft.Azure.Management.Resources) NuGet packages, and update the various constants for your subscription. All it does is list a couple of properties from the resource group but you whatever you want, provided your AAD app has the required roles.</p> <code>string clientId = &quot;&lt;Client ID for your AAD app&gt;&quot;;    <br />var subscriptionId = &quot;&lt;Your Azure SubscriptionId&gt;&quot;;     <br />string tenant = &quot;&lt;AAD tenant name&gt;&gt;.onmicrosoft.com&quot;;     <p></p>    <p>var authContext = new AuthenticationContext(string.Format(&quot;<a href="https://login.windows.net/{0}&quot;">https://login.windows.net/{0}&quot;</a>, tenant)); </p>    <p>X509Certificate2 cert = null;      <br />X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);       <br />string certName = &quot;MyAzureManagement&quot;;       <br />try       <br />{       <br />&#160;&#160;&#160; store.Open(OpenFlags.ReadOnly);       <br />&#160;&#160;&#160; var certCollection = store.Certificates;       <br />&#160;&#160;&#160; var certs = certCollection.Find(X509FindType.FindBySubjectName, certName, false);       <br />&#160;&#160;&#160; cert = certs[0];       <br />}       <br />finally       <br />{       <br />&#160;&#160;&#160; store.Close();       <br />} </p>    <p>var certCred = new ClientAssertionCertificate(clientId, cert);      <br />var token = authContext.AcquireToken(&quot;<a href="https://management.core.windows.net/&quot;">https://management.core.windows.net/&quot;</a>, certCred);       <br />var creds = new TokenCloudCredentials(subscriptionId, token.AccessToken);       <br />var client = new ResourceManagementClient(creds);       <br />var rg = client.ResourceGroups.Get(&quot;Default-Web-WestUS&quot;);       <br />Console.WriteLine(rg.ResourceGroup.Id);       <br />Console.WriteLine(rg.ResourceGroup.Location);       </p></code>]]></content:encoded>
    </item>
    <item>
      <title>So, you want to build a cloud service?</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/so-you-want-to-build-a-cloud-service</link>
      <pubDate>Tue, 02 Jun 2015 00:28:29 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/2015/06/02/so-you-want-to-build-a-cloud-service/</guid>
      <description><![CDATA[Great! Now, you probably have a million questions on which cloud provider, technologies and patterns...]]></description>
      <content:encoded><![CDATA[<p>Great! Now, you probably have a million questions on which cloud provider, technologies and patterns you should use. And while those questions are important, in this post I want to go through a few more fundamental questions which I’ve found that people often overlook or misunderstand. While each of these questions have technical implications, in many cases the right answer will be influenced more by your business strategy than any technical considerations.</p>  <h2>Your subscription or mine?</h2>  <p>Possibly the most fundamental question is whether the solution is intended to be run in your own cloud subscription, or in that of your customers. </p>  <p>A solution deployed into your own subscription is normally categorised as a Software as a Service (SaaS) application, as your customers can use it without needing to own or understand the underlying resources (VMs, databases, etc). This is probably the default model for most new cloud services, and it is increasingly the model expected by most customers (although probably not most enterprise IT buyers). Some of the key implications of this model are:</p>  <ul>   <li>You (as the owner of the service) need to directly pay for the resources used by the solution </li>    <li>Your team must be responsible for deploying and managing the production solution, not just building the code</li>    <li>You can upgrade, monitor and scale the solution as you wish, but you need to manage customer expectations around availability, data retention, privacy etc. </li>    <li>You won’t be able to cater for any customer requirements that you didn’t explicitly design into the solution.</li> </ul>  <p>The other option is to build a solution that is designed to be deployed to a customer’s own cloud subscription. This normally means packaging the solution as one or more VM images, although there are also ways of including PaaS components into such a solution. This approach is often the easiest way to take a “legacy” ISV solution to the cloud, but it can also be a good option for new solutions that may need to be customised or configured extensively by the end customer. Some of the implications of this model are:</p>  <ul>   <li>You (as the owner of the service) are not responsible for (and have no visibility of) the resources used by your solution </li>    <li>You have limited control over if and when customers apply patches or updates (you’ll need to build/leverage some kind of update service to do this). As such you may need to support multiple versions of your application for some time.</li>    <li>Customers have full control of the resources on which your application runs (VMs, databases, etc) and can tinker with or break them at will.</li> </ul>  <p>It’s worth noting that regardless of which option you choose you have the option to promote and monetise your application using the <a href="https://azure.microsoft.com/marketplace/">Azure Marketplace</a>. The “Application Services” category is for SaaS solutions that can be “logically” provisioned in a customer’s subscription (even though the resources run in your own subscription), while the “Virtual Machines” category is for VM images that are used to provision VMs directly within the customer’s subscription. </p>  <h2>IaaS or PaaS?</h2>  <p>Another key question is whether the application should be built on an infrastructure as a Service (IaaS) platform or on a Platform as a Service (PaaS) stack. </p>  <p>IaaS solutions consist of one or more Virtual Machines which you are responsible for deploying and managing. IaaS services are becoming a commodity, with many providers offering similar capabilities. This makes IaaS solutions relatively portable, but it can come at cost of increased complexity and additional management tasks. Some of the key implications of an IaaS based solution include:</p>  <ul>   <li>You can choose to use almost any technology in your solution (OSes, frameworks, programming languages, etc).</li>    <li>The solution can be made to run in different environments (e.g. on-premises, in different cloud providers, or in different users’ subscriptions) with minimal effort</li>    <li>You (as the VM owner) are responsible for the health of the VM from the OS up, including patching, monitoring, upgrading, etc.</li>    <li>Implementing High Availability and Disaster Recovery can be very complex.</li> </ul>  <p>PaaS is all about providing higher-level abstractions to developers (e.g. websites, queues, databases etc) that hide the details of the underlying Virtual Machines (even though they are still there). This can increase productivity and reduce complexity, but it comes at a cost of reduced flexibility. Some PaaS stacks can run on arbitrary IaaS platforms (allowing them to be run on multiple cloud providers), while others are tied to a single cloud provider. While some people get concerned about the potential for “lock in”, in practice very few solutions are moved from one cloud provider to another. Some of the implications of PaaS based solutions include:</p>  <ul>   <li>Features such as high availability, backup and autoscaling are often provided as a part of the service, eliminating additional complexity and costs</li>    <li>Components don’t need to be individually licensed or deployed</li>    <li>You will be constrained in which technologies you can use and how they can be configured</li>    <li>It may not be possible to run the solution outside of the platform it was designed for.</li> </ul>  <p>In Microsoft Azure, we’ve seen most people favour PaaS for new development, but they often choose IaaS for migrating existing solutions to the cloud. It’s also worth noting that IaaS vs PaaS is not a binary decision – in many cases the most sensible option is to mix and match the approaches for different solution components (for example, a PaaS Web App talking to a NoSQL database running on a VM). </p>  <h2>Single- or Multi-tenant?</h2>  <p>The final question for today is whether to go for a single-tenant model (where each customer’s solution components are deployed separately) or a multi-tenant model (where multiple customers are supported by the same set of components). </p>  <p>A single-tenant solution is the natural starting point for an existing on-premises app, or one that is designed to run in customers’ own subscriptions. Typically this results in a solution with one database for each customer, and it may also involve a separate website for each customer. These may be each deployed to their own VMs, or it may be possible to deploy multiple components for different customers to the same VMs. Some of the implications of a single-tenant solution include:</p>  <ul>   <li>The solution may make less efficient use of resources (compared to a multi-tenant solution) and hence cost more to run</li>    <li>Tenants can be deployed, upgraded and scaled independently. This increases flexibility but also significantly increases complexity. For example you could end up having different customer databases running with different versions of the schema.</li>    <li>Data and code is inherently isolated, making it unlikely that you will accidentally leak data across tenants. However this also makes it difficult to get aggregate data out of the solution for analytics or similar purposes.</li> </ul>  <p>Multi-tenant solutions generally require a bit more care to build, as you need to make sure that the solution supports all tenants while maintaining the illusion to your customers that they are the only ones using the app. However the solution should be easier to grow and manage long-term, and as such this is usually the default option for “born in the cloud” apps. Some of the implications include:</p>  <ul>   <li>The solution can make efficient use of resources, although you may need to be wary of “noisy neighbour” problems</li>    <li>The application will be deployed and managed centrally for all customers. This may limit flexibility for users, for example it won’t be possible for some users to remain on an older version.</li>    <li>You need to be extra careful to maintain data privacy by not sharing any data across tenants (since everyone’s data will be in the same database or other components).</li> </ul>  <h2>Conclusion</h2>  <p>If you’re building a brand new consumer-facing SaaS application, chances are you want to design a multi-tenant PaaS-based solution that is deployed into your own cloud subscription. However many businesses are dealing with challenges such as an existing code base, an existing customer base and evolving business strategies that can mean that other solutions may be more appropriate. In rare cases it may even make sense to build multiple versions of your solution supporting different models, but this should probably only be considered as a part of a short-term transition strategy. </p>  <p>I hope this post has given some useful insight into these questions and the reasons for choosing one model over another.</p>]]></content:encoded>
    </item>
    <item>
      <title>Azure QuickStarts: In Visual Studio and on the web</title>
      <link>https://docs.microsoft.com/archive/blogs/tomholl/azure-quickstarts-in-visual-studio-and-on-the-web</link>
      <pubDate>Sun, 10 May 2015 16:01:00 GMT</pubDate>
      <dc:creator><![CDATA[Tom Hollander]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/tomholl/2015/05/10/azure-quickstarts-in-visual-studio-and-on-the-web/</guid>
      <description><![CDATA[Whether you are a beginner or an experienced developer, learning a brand new API can be challenging....]]></description>
      <content:encoded><![CDATA[<p>Whether you are a beginner or an experienced developer, learning a brand new API can be challenging. Comprehensive documentation is obviously important, but sometimes that can make the new API seem even more daunting by surfacing everything the API can do when you&rsquo;re not sure how to approach the basics.</p>
<p>We&rsquo;ve found that one of the best ways to help people get productive with new APIs is through simple sample applications which demonstrate just the most critical scenarios, showing the correct implementation patterns with the smallest possible overhead. The samples help you understand what the API can do, allow you to debug through the code to understand how it works, and allow you to copy and paste the code into your own solutions with confidence.</p>
<p>Here at Microsoft we call these kinds of samples &ldquo;QuickStarts&rdquo;, and we&rsquo;ve included them in a number of products of the years, including many deliverables from the patterns &amp; practices team. We&rsquo;ve also included a number of QuickStarts with the Azure SDK for a few months now, but until now they may have been difficult to discover and use.</p>
<p>With the new <a href="https://azure.microsoft.com/blog/2015/04/29/announcing-the-azure-sdk-2-6-for-net/">Azure SDK 2.6 for .NET</a>, we&rsquo;ve made a few key changes to provide a better experience for the Azure QuickStarts. First, there are now two ways of finding them. If you&rsquo;re using Visual Studio and have the Azure SDK installed, you can see find the QuickStarts by choosing <strong>File | New Project</strong>, expanding the <strong>Templates \ Visual C# \ Cloud \ QuickStarts </strong>node and selecting the technology you&rsquo;re interested in:</p>
<p><img style="display: inline; border: 0px;" title="VSQuickStarts" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/43/90/metablogapi/1122.VSQuickStarts_3C3F6B59.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-43-90-metablogapi/1122.VSQuickStarts_5F00_3C3F6B59.png" alt="VSQuickStarts" width="941" height="701" border="0" /></p>
<p>If you&rsquo;re not currently using Visual Studio, you can find the exact same samples in the <a href="https://code.msdn.microsoft.com/Azure-QuickStarts-c1b66e38">MSDN Code Gallery sample pack</a>. On this site you can view the source code in each sample, download an individual sample or the full pack, as well as rate the samples and ask questions.</p>
<p>In addition to providing more ways to find the samples, we&rsquo;ve been working to improve both the quality and quantity. Each sample now comes with improved documentation as well as error handling to anticipate common mistakes such as forgetting to update a connection string. We&rsquo;ve also added new samples for technologies like Azure Active Directory and Azure Files, and we&rsquo;ll keep adding more over time.</p>
<p>If you have any questions or feedback on the Azure QuickStarts you can provide a comment below or use the Q&amp;A tab present on each sample in the Code Gallery.</p>]]></content:encoded>
    </item>
  </channel>
</rss>