<?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>Ralf und die Cloud</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/ralfwi/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/ralfwi/feed.xml</link>
    <description>Eine lose Folge von interessanten Themen rund um die Microsoft Azure Cloud und die darin angebotenen Services.</description>
    <lastBuildDate>Tue, 16 Apr 2019 06:16:25 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Deploy VMs from custom image</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/deploy-vms-from-custom-image</link>
      <pubDate>Wed, 11 Oct 2017 14:07:38 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=695</guid>
      <description><![CDATA[Back in the old times, you might have used CDs to deploy a new desktop or server on premise. Or...]]></description>
      <content:encoded><![CDATA[Back in the old times, you might have used CDs to deploy a new desktop or server on premise. Or maybe System Center or any other tools. You might have created a "Golden Image" fully prepared and customized, or you used a basic image and tools like System Center, DSC, Puppet or Chef to customize your image after deployment. Well, good news is that most of those options are available in the cloud, too. If you go for the later one, just use a base image provided by Azure and include an extension for DSC or other tools to customize. If you want to use your own image, here is how to do it...
<!--more-->

I published already an article touching this for Azure Service Manager deployments (see <a href="https://blogs.technet.microsoft.com/ralfwi/2016/02/04/verschieben-von-vms-in-azure-mit-powershell/">here (in German, I'm afraid)</a>, but meanwhile you should plan with the Azure Resource Manager for deployments and that's what I will show here:
<ul>
<li> we use an existing VM and customize it (just a little bit, feel free to experiment further)
<li> we prepare the VM to be used as an image
<li> we deploy a new VM based on that image by using a template
</ul>

We will do this with Windows, but it works for Linux in nearly the same way. Watch the end of this article for the differences!

Ready? Let's go!

<h2>Customize Image</h2>
As said we will use an existing VM, for this example the VM is named "<strong>golden</strong>" and is in the resourcegroup "<strong>winvm-rg</strong>". Best practice is to build a new VM from scratch and customize it. For your first tests you might just change some registry keys, add a folder or install a feature (I always use the telnet client for tests). Iterate your way and use the experience you already have from your on-premise history...

<h2>Prepare VM</h2>
Good news is that you can use good, old <code>sysprep.exe</code> (located in <code>%windir%\system32\sysprep</code>). Once you are finished with customization, start it, choose (as usual) the "out-of-box-experience", click "generalize", and "shutdown". Or use this commandline after switching to the directory: 
[code]
sysprep.exe /generalize /oobe /shutdown
[/code]

Your customized VM will be prepared and shut down. Remember: If you restart that VM again, you have to go through that process again.

This was the first part, what comes next is to prepare the VM as image. There are two ways to do this. One is to create an "unmanaged image", the other is - you guess it - a "managed image". We will start with the "managed image" and have a short look on the "unmanaged image" later.

First, stop the VM (yes, the VM is in a shutdown state, but is not stopped!):

[code]
Stop-AzureRmVM -ResourceGroupName winvm-rg -Name golden
[/code]

Second, generalize the OS disk:

[code]
Set-AzureRmVm -ResourceGroupName winvm-rg -Name golden -Generalized
[/code]

Verify that the VM is in a generalized state:

[code]
Get-AzureRmVm -ResourceGroupName winvm-rg -Name golden -Status
[/code]

You should see something like this:

[code highlight="4"]
(...)
  Code            : OSState/generalized
  Level           : Info
  DisplayStatus   : VM generalized
(...)
[/code]

We will go on from here creating a managed image. For information how to create an unmanaged image (and what's the difference) see further down.

For the next steps, we first need some information. 
<ol>
<li> the region where the image will be available, we will use <code>germanycentral</code>
<li> the OSType, we will use <code>Windows</code>
<li> the URI of the VHD of our "golden" VM
<li> a (new?) resourcegroup to store the image reference
</ol>

Let's look at no. 3. How do we get this information? Well, the URI is part of the storage profile of the VM, and we find it there under OSDisk and Vhd:

[code]
$temp = Get-AzureRmVm -ResourceGroupName winvm-rg -Name golden
$uri = $temp.StorageProfile.OSDisk.Vhd.Uri
[/code]

Copy the Uri or save it in a variable like we did.

If you want to use a central resource group for all images, create it:

[code]
New-AzureRmResourceGroup -name &quot;goldenimages&quot; -location germanycentral
[/code]

And now bring it all together to create the image:

[code]
$imgConf = New-AzureRmImageConfig -Location germanycentral
$imgConf = Set-AzureRmImageOsDisk -Image $imgConf -OSType Windows -OSState Generalized -BlobUri $uri
$image=New-AzureRmImage -ImageName &quot;TestImage&quot; -ResourceGroupName goldenimages -Image $imgConf
[/code]

Needless to say that you should choose a better name then "TestImage". If you look at <code>$image</code>, you see the Id of the new image, or use <code>Get-AzureRmImage</code> for a list of your images. We need this Id for our image reference in the template.

<h2>Deploy a new VM</h2>

Time to use it in a template. Very simple, just use a normal VM deployment template and change two things:

Define a variable with the Id from above (<code>$image.Id</code>), this will look someting like:
[code highlight="3"]
(...)
  &quot;variables&quot;: {
    &quot;imageID&quot;: &quot;/subscriptions/{...}/resourceGroups/goldenimages/providers/Microsoft.Compute/images/TestImage&quot;,
(...)
[/code]

Use this variable in the <code>Microsoft.Compute/virtualMachines</code> resource definition:

[code highlight="4"]
(...)
        &quot;storageProfile&quot;: {
          &quot;imageReference&quot;: {
            &quot;id&quot;: &quot;[variables('ImageId')]&quot;
          }
        }
(...)
[/code]

and there we go. If you combine this with a deployment link like explained <a href="https://blogs.technet.microsoft.com/ralfwi/2016/12/16/deployments-per-web/">in this blog article</a> you can build your own internal marketplace. Since I published the template used for this example <a href="https://github.com/gitralf/mytemplates/tree/master/101-vm-simple-windows-from-managed-image" target="_blank">on GitHub</a>, you can see (but not deploy) all of this for copy&amp;paste. The reason you cannot deploy is that you don't have access to my resources, especially the resourcegroup "goldenimages". To give access to a resourcegroup is left as an exercise to the reader :-).

<h2>Unmanaged Images</h2>

Unmanaged images are stored in a storage blob. The way to create them is similar to the one used above. Customize and sysprep the VM, stop it and generalize it.

Then create the image:

[code]
Save-AzureRmVmImage -ResourceGroupName winvm-rg -Name golden -DestinationContainerName &quot;winimages&quot; -VHDNamePrefix &quot;prefix&quot; -path c:\temp\filename.json
[/code]

This will copy the VHD from the VM into a new container inside the same storage account. After the command finished, you find a JSON file at the location you entered (here  <code>c:\temp\filename.json</code>) with some information, and under "resources"-"storageProfile"-"osDisk"-"image"-"uri" is the path to that image. The path is contructed like this:

[code]
https://{storageaccount}.blob.core.cloudapi.de/system/Microsoft.Compute/Images/{DestinationContainer}/{prefix}-osDisk...
[/code]

To use that image in a deployment, make sure that the container has appropriate Read Permissions, maybe copy the image to another container/storageaccount and modify your template as shown:

[code highlight="3,4,5,33,38"]
(...)
  &quot;variables&quot;: {
    &quot;srcImageUri&quot;: &quot;https://goldenimages.blob.core.cloudapi.de/windows/master-Win2012R2-v3.vhd&quot;,
    &quot;srcImageOS&quot;: &quot;Windows&quot;,
    &quot;ImageResourceName&quot;: &quot;myImage&quot;,
(...)
  &quot;resources&quot;: [
    {
      &quot;type&quot;: &quot;Microsoft.Compute/images&quot;,
      &quot;apiVersion&quot;: &quot;2016-04-30-preview&quot;,
      &quot;name&quot;: &quot;[variables('ImageResourceName')]&quot;,
      &quot;location&quot;: &quot;[resourceGroup().location]&quot;,
      &quot;properties&quot;: {
        &quot;storageProfile&quot;: {
          &quot;osDisk&quot;: {
            &quot;osType&quot;: &quot;[variables('srcImageOS')]&quot;,
            &quot;osState&quot;: &quot;Generalized&quot;,
            &quot;blobUri&quot;: &quot;[variables('srcImageUri')]&quot;,
            &quot;storageAccountType&quot;: &quot;Standard_LRS&quot;
          }
        }
      }
    },
(...)
    {
      &quot;apiVersion&quot;: &quot;2017-03-30&quot;,
      &quot;type&quot;: &quot;Microsoft.Compute/virtualMachines&quot;,
      &quot;name&quot;: &quot;[variables('vmName')]&quot;,
      &quot;location&quot;: &quot;[resourceGroup().location]&quot;,
      &quot;dependsOn&quot;: [
        &quot;[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]&quot;,
        &quot;[resourceId('Microsoft.Network/networkInterfaces/', variables('nicName'))]&quot;,
        &quot;[resourceId('Microsoft.Compute/images/', variables('ImageResourceName'))]&quot;
      ],
      &quot;properties&quot;: {
        &quot;storageProfile&quot;: {
          &quot;imageReference&quot;: {
            &quot;id&quot;: &quot;[resourceId('Microsoft.Compute/images/', variables('ImageResourceName'))]&quot;
          }
        },
(...)
[/code]

To explain this: We create an additional resource, the image, and use this resource later for the VM. Notice that I moved the image to a new location in another storage account. You find the full template also <a href="https://raw.githubusercontent.com/gitralf/mytemplates/master/101-vm-simple-windows-from-image/azuredeploy.json" target="_blank">on GitHub</a>.

<h2>Additions</h2>
<h3>Linux</h3>
As promised, here is in short what to do with Linux. It is nearly identical, just replace the sysprep stuff with waagent.

[code]
sudo waagent -deprovision+user -force
[/code]

and replace "Windows" with "Linux" when creating the image. See further down unter "Links" for more information.

<h3>Preparations</h3>
Another thing is <em>where</em> you customize your VM. I prefer Azure, but using a Hyper-V image is also possible of course. You have to do some adoptions, especially watch out for using a fixed disk and a .vhd instead of a .vhdx, but after uploading the VHD into Azure storage the rest of the steps are the same. For preparation see further down under "Links".

<h3>Post-deployment tasks</h3>
Of course you can add extensions to the VM for DSC etc to customize the VM even further. Sometimes it's a thin line between putting everything into the image or keep some customizations for DSC. Discussions about this is far beyond the scope of this article...


<h2>Links</h2>
Here are some links to really good AzureDocs articles about image creation, including PowerShell and CLI creation of VMs from managed and unmanaged images. Check them out, they are worth it...

<ul>
<li><a href="https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sa-copy-generalized" target="_blank">Windows image</a>
<li><a href="https://docs.microsoft.com/en-us/azure/virtual-machines/windows/upload-generalized-managed" target="_blank">Managed image</a>
<li><a href="https://docs.microsoft.com/en-us/azure/virtual-machines/linux/tutorial-custom-images" target="_blank">Linux image</a>
<li><a href="https://docs.microsoft.com/en-us/azure/virtual-machines/windows/prepare-for-upload-vhd-image" target="_blank">Prepare VHD for upload</a>
</ul>

All examples are tested in Azure Germany. Here is where you get a <a href="https://azure.microsoft.com/en-us/free/germany/?ralfwi">free Azure Germany trial</a>!

]]></content:encoded>
    </item>
    <item>
      <title>Adding tags to resources</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/adding-tags-to-resources</link>
      <pubDate>Thu, 05 Oct 2017 03:08:41 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=685</guid>
      <description><![CDATA[Adding tags to resources helps us to organize our resources and to automate some tasks. Here is a...]]></description>
      <content:encoded><![CDATA[Adding tags to resources helps us to organize our resources and to automate some tasks. Here is a small intro on how to use tags in the Azure portal, in templates, or with PowerShell. In this article we will use tags only for virtual machines, but you can add tags to any resource created with the Azure Resource Manager (including resourcegroups).

Tags are simple name-value pairs that we can define the way we like it. An (often used) example is the tagging of a resource with the appropriate cost center (like "Department" = "Sales"), or to separate development from production ("Environment" = "Test"), or whatever else we have in mind to organize.

<!--more-->
<h2>Portal</h2>
If you look at the resource in the portal, there is a "Tags" entry. Click on it (1), enter a name-value pair (2) and hit "Save" (3). That's it. You can choose from previously used tags by selecting from the dropdown menu.

[caption id="attachment_686" align="alignnone" width="1024"]<a href="https://msdnshared.blob.core.windows.net/media/2017/09/tags-portal.jpg"><img width="1024" height="378" class="size-large wp-image-686" alt="using tags" src="https://msdnshared.blob.core.windows.net/media/2017/09/tags-portal-1024x378.jpg" /></a> Using tags in the Azure portal[/caption]

Here we use the name "purpose" and the value "blog-example" to make it easier to delete resources that we needed for screenshots... :-)

We can add more tags to a resource, up to 15 per resource. And there is a main "Tags" menu (1) that we can use to search across all our resources for special tags (2):

<a href="https://msdnshared.blob.core.windows.net/media/2017/09/tags-portal-2.jpg"><img width="1024" height="292" class="alignnone size-large wp-image-687" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/09/tags-portal-2-1024x292.jpg" /></a>
<h2>Templates</h2>
When using templates to deploy resources (which is a very good idea by the way...) simply add them (as a list) into the template:

[code gutter="false"]
...
    &quot;resources&quot;: [
    {
      &quot;apiVersion&quot;: &quot;2016-01-01&quot;,
      &quot;type&quot;: &quot;Microsoft.Storage/storageAccounts&quot;,
      &quot;name&quot;: &quot;[concat('storage', uniqueString(resourceGroup().id))]&quot;,
      &quot;location&quot;: &quot;[resourceGroup().location]&quot;,
      &quot;tags&quot;: {
        &quot;Dept&quot;: &quot;Finance&quot;,
        &quot;Environment&quot;: &quot;Production&quot;
      },
...
[/code]

Here we set two tags, "Dept" and "Environment", at a storage resource. This is really simple... and of course those resources and tags can be found via the portal also. If you use parameters for the tags, you will be - as usual - prompted before the deployment to enter values for those tags.

[code gutter="false"]
...
    &quot;resources&quot;: [
    {
      &quot;apiVersion&quot;: &quot;2016-01-01&quot;,
      &quot;type&quot;: &quot;Microsoft.Storage/storageAccounts&quot;,
      &quot;name&quot;: &quot;[concat('storage', uniqueString(resourceGroup().id))]&quot;,
      &quot;location&quot;: &quot;[resourceGroup().location]&quot;,
      &quot;tags&quot;: {
        &quot;Dept&quot;: &quot;[parameters('DepartmentTag')]&quot;,
        &quot;Environment&quot;: &quot;[parameters('EnvironmentTag')]&quot;
      },
...
[/code]

Note that it is not possible to set tags on resourcegroups with a template, only to resources. If you want to add tags to resourcegroups, you must do this via the portal or PowerShell.


<h2>PowerShell</h2>

Tags are a property of a resource or a resourcegroup object. To list them, simply enter <code> Get-AzureRmResourceGroup </code> and you might get something like this:
[code gutter="false"]
C:&gt; Get-AzureRmResourceGroup -Name test01-rg

ResourceGroupName : test01-rg
Location          : germanycentral
ProvisioningState : Succeeded
Tags              :
                    Name     Value
                    =======  ===============
                    purpose  unknown
                    status   unknown/unknown

ResourceId        : /subscriptions/06(...)/resourceGroups/test01-rg
[/code]

Similar for a resource inside a resourcegroup:

[code gutter="false"]
C:&gt; Get-AzureRmResource -ResourceGroupName test01-rg -ResourceName rw01-vm1

Name              : rw01-vm1
ResourceId        : /subscriptions/06(...)/virtualMachines/rw01-vm1
ResourceName      : rw01-vm1
ResourceType      : Microsoft.Compute/virtualMachines
ResourceGroupName : test01-rg
Location          : germanycentral
SubscriptionId    : 06d(...)
Tags              : {purpose}
[/code]

Tags are dictionary objects, so we can work with them once we saved the object:

[code gutter="false"]
C:&gt; $tags = (Get-AzureRmResource -ResourceGroupName test01-rg -ResourceName rw01-vm1).Tags
C:&gt; $tags

Name                           Value
----                           -----
purpose                        VM
[/code]

We can add name/value pairs:
[code gutter="false"]
C:&gt; $tags.Add(&quot;status&quot;,&quot;active&quot;)
C:&gt; $tags

Name                           Value
----                           -----
purpose                        VM
status                         active
[/code]

We can modify them (like adding some text):

[code gutter="false"]
C:&gt; $tags.Item(&quot;status&quot;) += &quot; (do not delete)&quot;
C:&gt; $tags

Name                           Value
----                           -----
purpose                        VM
status                         active (do not delete)
[/code]

or we can delete pairs:

[code gutter="false"]
C:&gt; $tags.Remove(&quot;status&quot;)
[/code]

Once we are finished with modifying the dictionary object, we can write it back by using the "Set- instead of the "Get-" cmdlet:
[code gutter="false"]
C:&gt; Set-AzureRmResource -ResourceGroupName test01-rg -ResourceName rw01-vm1 -Tags $tags

C:&gt; Set-AzureRmResourceGroup -Name test01-rg -Tags $tags
[/code]

<h2>Summary</h2>
Tags are a great way to organize resources just the way you like it. And if you think further, it is easy to assign tasks to all resources with a special value for a key, like garbage collection for all resources with <code>"Status" = "delete me"</code>. 

To be consequent, use Azure policies to make sure that all your resources or resourcegroups at least have a tag.]]></content:encoded>
    </item>
    <item>
      <title>Azure Instance Metadata</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/azure-instance-metadata</link>
      <pubDate>Tue, 22 Aug 2017 21:31:13 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=655</guid>
      <description><![CDATA[There is a great new feature Azure provides, called Azure Instance Metadata. It gives you...]]></description>
      <content:encoded><![CDATA[There is a great new feature Azure provides, called<strong> Azure Instance Metadata</strong>. It gives you information about the metadata of a VM <strong>from within</strong> the VM. And here is how...

<!--more-->

At the time of this writing Azure Instance Metadata is GA already in global Azure, and it's in preview in all the other clouds (and therefore available). Let's see how it works by using one of those others, the Azure German Cloud in this case.

Log into your VM (Windows or Linux is both supported, I'm using a Windows VM here). Now start a PowerShell, and enter:

[code light="true" autolinks="false"]
curl –H @{‘Metadata’=’true’} https://169.254.169.254/metadata/instance?api-version=2017-04-02
[/code]

We are using <code>curl</code> here (which is by the way an alias for <code>Invoke-WebRequest)</code>. You might also use this syntax:

[code light="true" autolinks="false"]
Invoke-RestMethod -Headers @{&quot;Metadata&quot;=&quot;true&quot;} `
  -URI https://169.254.169.254/metadata/instance?api-version=2017-04-02
[/code]

There are a couple of things in this request. Let's see them one by one:
<ul>
 	<li>The request must include the header <code>"Metadata"="true"</code></li>
 	<li>The address is always 169.254.169.254 and is accessible <strong>only from within</strong> the VM. There is no need to assign that address to your VM, this is done automatically.</li>
 	<li>Metadata format is versioned, here we use version <code>2017-04-02</code>. Other version will come, but you can still use the older versions</li>
</ul>
The output has a "Content" field with a json string containing the metadata. Json is the default output format, text is also possible, example see further down.

[code autolinks="false"]
{
    &quot;compute&quot;:{
        &quot;location&quot;:&quot;GermanyCentral&quot;,
        &quot;name&quot;:&quot;myVM&quot;,
        &quot;offer&quot;:&quot;WindowsServer&quot;,
        &quot;osType&quot;:&quot;Windows&quot;,
        &quot;platformFaultDomain&quot;:&quot;0&quot;,
        &quot;platformUpdateDomain&quot;:&quot;0&quot;,
        &quot;publisher&quot;:&quot;MicrosoftWindowsServer&quot;,
        &quot;sku&quot;:&quot;2016-Datacenter&quot;,
        &quot;version&quot;:&quot;2016.127.20170630&quot;,
        &quot;vmId&quot;:&quot;ef2aa96c-e3a7-5432-b42f-d31fc94a3cec&quot;,
        &quot;vmSize&quot;:&quot;Standard_DS2_v2&quot;
    },
    &quot;network&quot;:{
        &quot;interface&quot;:[
            {
                &quot;ipv4&quot;:{
                    &quot;ipAddress&quot;:[
                        {
                            &quot;privateIpAddress&quot;:&quot;192.168.1.4&quot;,
                            &quot;publicIpAddress&quot;:&quot;51.4.201.56&quot;
                        }
                    ],
                    &quot;subnet&quot;:[
                        {
                            &quot;address&quot;:&quot;192.168.1.0&quot;,
                            &quot;prefix&quot;:&quot;24&quot;
                        }
                    ]
                },
                &quot;ipv6&quot;:{
                    &quot;ipAddress&quot;:[]
                },
                &quot;macAddress&quot;:&quot;0017FA104DA4&quot;
            }
        ]
    }
}
[/code]

The meaning of the most of the fields should be clear, see the complete description on the <a target="_blank" href="https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service" rel="noopener">Azure Documentation website</a>. You can access parts of this output in different ways, here are some examples...

[code light="true" autolinks="false"]
(Invoke-RestMethod -Headers @{&quot;Metadata&quot;=&quot;true&quot;} `
  -URI &quot;https://169.254.169.254/metadata/instance?api-version=2017-04-02).compute.location&quot;
[/code]

This will retrieve the metadata as an object and access the nodes "Compute" and "Location" under it directly. So the output is as expected:

[code light="true" autolinks="false"]
GermanyCentral
[/code]

Or save the output to a variable first and then access the properties for the same result
[code]
$md = Invoke-RestMethod -Headers @{&quot;Metadata&quot;=&quot;true&quot;} `
  -URI &quot;https://169.254.169.254/metadata/instance?api-version=2017-04-02&quot;
$md.Compute.Location
[/code]

Or put it directly in the request (when you expect a single property you have to use the "text" format by appending "format=text"):
[code light="true" autolinks="false"]
Invoke-RestMethod -Headers @{&quot;Metadata&quot;=&quot;true&quot;} `
  -URI &quot;https://169.254.169.254/metadata/instance/compute/location?api-version=2017-04-02&amp;format=text&quot;
[/code]

Furthermore, there is not only metadata about the environment, but also about upcoming maintenance events. Simply replace "instance" with "scheduledevents":
[code light="true" autolinks="false"]
Invoke-RestMethod -Headers @{&quot;Metadata&quot;=&quot;true&quot;} `
  -URI &quot;https://169.254.169.254/metadata/scheduledevents?api-version=2017-04-02&quot;
[/code]

Please remember that in some regions this service is still in preview, and watch out for announcements for example in the Azure Germany Teamblog (see link on the right menue) or in the Azure blog.

If you want to test it in Azure Germany, why not <a href="https://azure.microsoft.com/de-de/free/germany/?ralfwi" target="_blank">sign up for a free trial account</a>?]]></content:encoded>
    </item>
    <item>
      <title>Bedingte Verzweigungen Teil 2</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/bedingte-verzweigungen-teil-2</link>
      <pubDate>Tue, 15 Aug 2017 12:19:30 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=645</guid>
      <description><![CDATA[(also available in english)

Wie angekündigt in meinem letzten Artikel zu ARM Templates folgt hier...]]></description>
      <content:encoded><![CDATA[(also <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/15/another-if-then-in-arm-templates/">available in english</a>)

Wie angekündigt in <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/10/bedingte-verzweigungen-mit-arm-templates/">meinem letzten Artikel zu ARM Templates </a>folgt hier eine weitere Möglichkeit, in einem ARM Template zu verzweigen, basierend auf einer Bedingung. Wir verwenden wieder die Idee mit den verschachtelten Deployments, aber diesmal füllen wir einen Array mit alternativen URLs und entscheiden uns - mit etwas Mathe-Magie - für eines der Arrayelemente.

<!--more-->

Für unser Beispiel möchten wir uns zwischen einem Availability Set (bei mehr als einer VM) oder keinem Availability Set (bei nur einer VM) entscheiden. Die Bedingung ist also klar. Aber leider gibt es sowas wie <code>if (vmCount -gt 1) then...</code> hier nicht. Ok, hier ist der Trick (und Danke an AzureCAT für die Demonstration in den <a href="https://github.com/mspnp/template-building-blocks" target="_blank">buildingblock templates </a>). Es gibt eine Funktion namens "mod", steht für das lateinische "modulo", und liefert den Rest bei einer Division zweier ganzer Zahlen. Wenn man zum Beispiel "8" durch "5" teilt, dann ergibt das "1" und ein Rest von "3", eben <code>mod(8,5)=3</code>. Wie uns das hilft? Ganz einfach. Teilt man eine Zahl durch die nächstkleinere, dann ergibt das immer einen Rest von "1", außer die erste Zahl ist eine "2". Wie man hier sehen kann:

[code gutter="false"]
mod(2,1)=0
mod(3,2)=1
mod(4,3)=1
mod(5,4)=1
...
[/code]

Also ganz egal, was unsere Zahl ist, wir bekommen entweder eine "0" oder eine "1". Und jetzt? Na, bitte mitdenken! Ich hab doch oben schon verraten, dass wir mit einem Array arbeiten wollen, und hier haben wir etwas, dass sich prima als Index eignet...

Hier mal der erste Teil des Templates (<a href="https://github.com/gitralf/templates/tree/master/test-conditional-2" target="_blank">gibt es auch auf GitHub</a> mit dem Rest für diese Demo):
[code]
{
  &quot;$schema&quot;: &quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&quot;,
  &quot;contentVersion&quot;: &quot;1.0.0.0&quot;,
  &quot;parameters&quot;: {
        &quot;vmCount&quot;: { 
          &quot;type&quot;: &quot;int&quot; 
        }
  },
   
  &quot;variables&quot;: {
    &quot;index&quot;: &quot;[mod(add(parameters('vmCount'), 1), parameters('vmCount'))]&quot;,
    &quot;avSetTemplates&quot;: [
      &quot;[uri(deployment().properties.templateLink.uri, 'availabilitySet-empty.json')]&quot;,
      &quot;[uri(deployment().properties.templateLink.uri, 'availabilitySet-new.json')]&quot;
    ],
    &quot;avSetTemplate&quot;: &quot;[variables('avSetTemplates')[variables('index')]]&quot;,
    &quot;templateNamePart&quot;: [
      &quot;nop&quot;,
      &quot;new&quot;
    ],
    &quot;deploymentName&quot;: &quot;[variables('templateNamePart')[variables('index')]]&quot;
  },
[/code]

Keine Panik! Wir schauen uns das im Einzelnen an, dieser nette Trick ist es wert...

Wir haben einen Parameter "vmCount" als Integer (Zeilen 5-7). Geben wir eine "1" vor, dann brauchen wir kein Availability Set, aber für alles größer als "1" wollen wir das. Die erste Variable, die wir definieren, ist "index" (Zeile 11), und die regelt das. Wir addieren "1" zu "vmCount", teilen durch "vmCount", und erhalten als Rest (wir erinnern uns an unserer Tabelle von oben) eine "0" bei "vmCount" von "1" (<code>mod(1+1,1)=0</code>) oder eine "1", wenn "vmCount" größer als "1" ist (<code>mod(2+1,2)=1</code>).

Als nächstes definieren wir einen Array mit den alternativen URLs, basierend auf der aktuellen URL. Für unser Beispiel verwenden wir das Template "availabilitySet-empty.json" (ein leeres Template, wir wollen ja nix tun) und "availabilitySet-new.json" (hier würde dann die Definition für das Availability Set stehen  - in unserem Beispiel ist das aber ebenfalls leer).

Jetzt fügen wir alles zusammen (Zeile 16) und definieren das nächste zu verwendende Template als das Element aus dem Array mit dem Index, den wir gerade ausgerechnet haben. Wir speichern das in der Variable "avSetTemplate" (ohne "s" am Ende). Und (Zeilen 17-21) dann definieren wir noch einen Deployment Namen, der auf dem gleichen Index basiert. Hier ist der Rest des Templates:

[code firstline="24"]
  &quot;resources&quot;: [
    {
      &quot;type&quot;: &quot;Microsoft.Resources/deployments&quot;,
      &quot;apiVersion&quot;: &quot;2015-01-01&quot;,
      &quot;name&quot;: &quot;[variables('deploymentName')]&quot;,
      &quot;properties&quot;: {
        &quot;mode&quot;: &quot;Incremental&quot;,
        &quot;templateLink&quot;: { &quot;uri&quot;: &quot;[variables('avSetTemplate')]&quot; },
        &quot;parameters&quot;: {
        }
      }
    }
  ],

  &quot;outputs&quot;: {
  }
}
[/code]

Zeit für einen (verbose) Test...

[code gutter="false" highlight="1,2,8"]
PS C:\temp&gt; New-AzureRmResourceGroupDeployment -vmcount 1 -verbose -Name &quot;conditional11&quot; -ResourceGroupName vstest1-mcd  
-TemplateUri &quot;https://raw.githubusercontent.com/gitralf/templates/master/test-conditional-2/azuredeploy.json&quot; 

AUSFÜHRLICH: Ausführen des Vorgangs &quot;Creating Deployment&quot; für das Ziel &quot;vstest1-mcd&quot;.
AUSFÜHRLICH: 10:45:40 - Template is valid.
AUSFÜHRLICH: 10:45:40 - Create template deployment 'conditional11'
AUSFÜHRLICH: 10:45:40 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:45:45 - Resource Microsoft.Resources/deployments 'nop' provisioning status is running
AUSFÜHRLICH: 10:45:45 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:45:51 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:45:56 - Resource Microsoft.Resources/deployments 'nop' provisioning status is succeeded


DeploymentName          : conditional11
ResourceGroupName       : vstest1-mcd
ProvisioningState       : Succeeded
Timestamp               : 15.08.2017 08:45:52
Mode                    : Incremental
TemplateLink            :
                          Uri            : https://raw.githubusercontent.com/gitralf/templates/master/test-conditional-2/azuredeploy.json
                          ContentVersion : 1.0.0.0

Parameters              :
                          Name             Type                       Value
                          ===============  =========================  ==========
                          vmCount          Int                        1
[/code]

"vmCount" haben wir in der Kommandozeile auf "1" gesetzt, wir brauchen also kein Availability Set (Deployment "nop"). Setzen wir "vmCount" auf "2", erhalten wir:

[code gutter="false" highlight="1,2,8"]
PS C:\temp&gt; New-AzureRmResourceGroupDeployment -vmcount 2 -verbose -Name &quot;conditional11&quot; -ResourceGroupName vstest1-mcd
-TemplateUri &quot;https://raw.githubusercontent.com/gitralf/templates/master/test-conditional-2/azuredeploy.json&quot; 

AUSFÜHRLICH: Ausführen des Vorgangs &quot;Creating Deployment&quot; für das Ziel &quot;vstest1-mcd&quot;.
AUSFÜHRLICH: 10:46:11 - Template is valid.
AUSFÜHRLICH: 10:46:12 - Create template deployment 'conditional11'
AUSFÜHRLICH: 10:46:12 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:46:17 - Resource Microsoft.Resources/deployments 'new' provisioning status is running
AUSFÜHRLICH: 10:46:17 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:46:22 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:46:28 - Resource Microsoft.Resources/deployments 'new' provisioning status is succeeded


DeploymentName          : conditional11
ResourceGroupName       : vstest1-mcd
ProvisioningState       : Succeeded
Timestamp               : 15.08.2017 08:46:27
Mode                    : Incremental
TemplateLink            :
                          Uri            : https://raw.githubusercontent.com/gitralf/templates/master/test-conditional-2/azuredeploy.json
                          ContentVersion : 1.0.0.0

Parameters              :
                          Name             Type                       Value
                          ===============  =========================  ==========
                          vmCount          Int                        2
[/code]

Wie erwartet wird das Deployment "new" verwendet.

Das war's auch schon. Ich bin mir sicher, dass - zusammen mit meinem anderen Artikel zu <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/10/bedingte-verzweigungen-mit-arm-templates/">Bedingten Verzweigungen in ARM Templates </a>- der Weg offen steht für neue Templates und Deployments. Und nochmal Danke an AzureCAT!

Wer das testen möchte und zu den wenigen gehört, die noch kein Azure Account haben, hier ist zum Beispiel ein Link zu einem <a href="https://azure.microsoft.com/de-de/free/germany/?ralfwi">kostenlosen Testabo für Azure Deutschland</a>.

]]></content:encoded>
    </item>
    <item>
      <title>another if...then in ARM templates</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/another-if-then-in-arm-templates</link>
      <pubDate>Tue, 15 Aug 2017 11:47:04 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=635</guid>
      <description><![CDATA[(auch verfügbar in Deutsch)

As promised in my last article about ARM templates here is another...]]></description>
      <content:encoded><![CDATA[(auch <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/15/bedingte-verzweigungen-teil-2/">verfügbar in Deutsch</a>)

As promised in <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/10/if-then-in-arm-templates/" target="_blank">my last article about ARM templates</a> here is another way to trigger a deployment based on a condition. We again use the idea of having nested deployments, but this time we fill an array with alternative template links and decide - based on math - which one to use.

<!--more-->

In this example we want to decide if an availability set should be created or not. This obviously only makes sense when we have more than one VM, so this will be our condition. But how to use it? There is nothing like <code>if (vmCount -gt 1) then...</code>. Well, here is the trick (and thanks to the <a href="https://github.com/mspnp/template-building-blocks" target="_blank">AzureCAT buildingblock templates </a>for the idea). There is a function called "mod", which stands for the latin "modulo" and returns the rest when you divide two numbers. For example, when you divide "8" by "5", you get "1" and a rest of "3", so <code>mod(8,5)=3</code>. How does this help us? Easy. When you divide a number by the next smaller one, you always get a rest of "1", except when the first number is a "2". See here:

[code gutter="false"]
mod(2,1)=0
mod(3,2)=1
mod(4,3)=1
mod(5,4)=1
...
[/code]

No matter what your number is, when you use the "mod" function with the next smaller number, you either get "0" or "1". Any idea, how to go on? You should! I already taked about using an array, and here we have something that we can use as an index... Have a look at this part of a template (<a href="https://github.com/gitralf/templates/tree/master/test-conditional-2" target="_blank">available on GitHub</a> with the rest for this demo):
[code]
{
  &quot;$schema&quot;: &quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&quot;,
  &quot;contentVersion&quot;: &quot;1.0.0.0&quot;,
  &quot;parameters&quot;: {
        &quot;vmCount&quot;: { 
          &quot;type&quot;: &quot;int&quot; 
        }
  },
   
  &quot;variables&quot;: {
    &quot;index&quot;: &quot;[mod(add(parameters('vmCount'), 1), parameters('vmCount'))]&quot;,
    &quot;avSetTemplates&quot;: [
      &quot;[uri(deployment().properties.templateLink.uri, 'availabilitySet-empty.json')]&quot;,
      &quot;[uri(deployment().properties.templateLink.uri, 'availabilitySet-new.json')]&quot;
    ],
    &quot;avSetTemplate&quot;: &quot;[variables('avSetTemplates')[variables('index')]]&quot;,
    &quot;templateNamePart&quot;: [
      &quot;nop&quot;,
      &quot;new&quot;
    ],
    &quot;deploymentName&quot;: &quot;[variables('templateNamePart')[variables('index')]]&quot;
  },
[/code]

Don't be shocked when you don't understand it first, we go through it here. This awesome trick is worth it...

We have a parameter "vmCount" as an integer (line 5-7). When we enter "1" as number of VMs, we don't need an availability set, but for "vmCount" greater than "1" we want it. The first variable we create is "index" (line 11). We add "1" to "vmCount" and divide it by "vmCount". According to our table this gives us a "0" when vmCount is "1" (<code>mod(1+1,1)=0</code>) or "1" (<code>mod(2+1,2)=1</code>) when "vmCount" is greater than "1".

Next step (lines 12-15) is to define an array of alternative URLs, based on the current URL. For our example we (like in the last article) use two empty templates and name them "availabilitySet-empty.json" (this would be the empty template to do nothing) and "availabilitySet-new.json" (which would normally hold the definition for the availability set - in our example it is empty, too).

Now we put it together (line 16) and define the next template where the deployment should continue as the element in this array with the index that we calculated, and store it in variable "avSetTemplate" (without the "s" at the end). And (line 17-21) we define a deployment name also based on an array using the same index again. Here is the rest of the template:

[code firstline="24"]
  &quot;resources&quot;: [
    {
      &quot;type&quot;: &quot;Microsoft.Resources/deployments&quot;,
      &quot;apiVersion&quot;: &quot;2015-01-01&quot;,
      &quot;name&quot;: &quot;[variables('deploymentName')]&quot;,
      &quot;properties&quot;: {
        &quot;mode&quot;: &quot;Incremental&quot;,
        &quot;templateLink&quot;: { &quot;uri&quot;: &quot;[variables('avSetTemplate')]&quot; },
        &quot;parameters&quot;: {
        }
      }
    }
  ],

  &quot;outputs&quot;: {
  }
}
[/code]

Time for a (verbose) test...

[code gutter="false" highlight="1,2,8"]
PS C:\temp&gt; New-AzureRmResourceGroupDeployment -vmcount 1 -verbose -Name &quot;conditional11&quot; -ResourceGroupName vstest1-mcd  
-TemplateUri &quot;https://raw.githubusercontent.com/gitralf/templates/master/test-conditional-2/azuredeploy.json&quot; 

AUSFÜHRLICH: Ausführen des Vorgangs &quot;Creating Deployment&quot; für das Ziel &quot;vstest1-mcd&quot;.
AUSFÜHRLICH: 10:45:40 - Template is valid.
AUSFÜHRLICH: 10:45:40 - Create template deployment 'conditional11'
AUSFÜHRLICH: 10:45:40 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:45:45 - Resource Microsoft.Resources/deployments 'nop' provisioning status is running
AUSFÜHRLICH: 10:45:45 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:45:51 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:45:56 - Resource Microsoft.Resources/deployments 'nop' provisioning status is succeeded


DeploymentName          : conditional11
ResourceGroupName       : vstest1-mcd
ProvisioningState       : Succeeded
Timestamp               : 15.08.2017 08:45:52
Mode                    : Incremental
TemplateLink            :
                          Uri            : https://raw.githubusercontent.com/gitralf/templates/master/test-conditional-2/azuredeploy.json
                          ContentVersion : 1.0.0.0

Parameters              :
                          Name             Type                       Value
                          ===============  =========================  ==========
                          vmCount          Int                        1
[/code]

We set the "vmCount" to "1", so no availability set was needed (deployment "nop"). Using a "vmCount" of "2" (or "42") gives us:

[code gutter="false" highlight="1,2,8"]
PS C:\temp&gt; New-AzureRmResourceGroupDeployment -vmcount 2 -verbose -Name &quot;conditional11&quot; -ResourceGroupName vstest1-mcd
-TemplateUri &quot;https://raw.githubusercontent.com/gitralf/templates/master/test-conditional-2/azuredeploy.json&quot; 

AUSFÜHRLICH: Ausführen des Vorgangs &quot;Creating Deployment&quot; für das Ziel &quot;vstest1-mcd&quot;.
AUSFÜHRLICH: 10:46:11 - Template is valid.
AUSFÜHRLICH: 10:46:12 - Create template deployment 'conditional11'
AUSFÜHRLICH: 10:46:12 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:46:17 - Resource Microsoft.Resources/deployments 'new' provisioning status is running
AUSFÜHRLICH: 10:46:17 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:46:22 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 10:46:28 - Resource Microsoft.Resources/deployments 'new' provisioning status is succeeded


DeploymentName          : conditional11
ResourceGroupName       : vstest1-mcd
ProvisioningState       : Succeeded
Timestamp               : 15.08.2017 08:46:27
Mode                    : Incremental
TemplateLink            :
                          Uri            : https://raw.githubusercontent.com/gitralf/templates/master/test-conditional-2/azuredeploy.json
                          ContentVersion : 1.0.0.0

Parameters              :
                          Name             Type                       Value
                          ===============  =========================  ==========
                          vmCount          Int                        2
[/code]

As expected the deployment "new" was used, since we want an availability set here.

That's it. I'm sure that - together with the last article about <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/10/if-then-in-arm-templates/" target="_blank">if...then in ARM templates</a> - you will find ways to use this nice little trick, and again thanks to the AzureCAT team for demonstrating it.

If you want to test it and you are one of the few guys out there without an Azure subscription, here for example is where you get a <a href="https://azure.microsoft.com/en-us/free/germany/?ralfwi">free Azure Germany trial</a>!]]></content:encoded>
    </item>
    <item>
      <title>Bedingte Verzweigungen mit ARM Templates</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/bedingte-verzweigungen-mit-arm-templates</link>
      <pubDate>Thu, 10 Aug 2017 16:39:24 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=615</guid>
      <description><![CDATA[(also available in english)

Wer häufiger mit ARM Templates arbeitet, der kommt früher oder später...]]></description>
      <content:encoded><![CDATA[(also available <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/10/if-then-in-arm-templates/">in english</a>)

Wer häufiger mit ARM Templates arbeitet, der kommt früher oder später an eine Stelle, wo ein if...then sinnvoll wäre, oder, mit anderen Worten, eine bedingte Verzweigung. Nun, es gibt ein paar Möglichkeiten, und in diesem Beitrag werden wir uns welche davon ansehen.

<!--more-->

<h2>Condition</h2>

Der wahrscheinlich einfachste Weg ist die Verwendung von <code>condition</code> für eine Ressource. Bei der Definition einer Ressource wird die Direktive einfach irgendwo innerhalb des Ressourcenblocks eingefügt, der besseren Lesbarkeit wegen hier am Anfang:
 
[code]
      &quot;condition&quot;: &quot;&lt;true-or-false&gt;&quot;,
      &quot;type&quot;: &quot;&lt;rpnamespace/type&gt;&quot;,
      &quot;apiVersion&quot;: &quot;&lt;api-version&gt;&quot;,
...
[/code]

Es ist so einfach wie es aussieht: Falls der Ausdruck hinter dem condition-Element ein True ergibt, wird die Ressource verteilt (natürlich nur, wenn de Rest der Syntax stimmt...), oder eben nicht. Hier ein oft verwendetes Beispiel:

[code]
    &quot;condition&quot;: &quot;[equals(parameters('sshOrPW'),'password')]&quot;,
    &quot;apiVersion&quot;: &quot;2016-03-30&quot;,
    &quot;type&quot;: &quot;Microsoft.Compute/virtualMachines&quot;,
    ...
},
{
    &quot;condition&quot;: &quot;[equals(parameters('sshOrPW'),'ssh')]&quot;,
    &quot;apiVersion&quot;: &quot;2016-03-30&quot;,
    &quot;type&quot;: &quot;Microsoft.Compute/virtualMachines&quot;,
    ...
},
[/code]

Wir brauchen nur einen Parameter mit den zwei möglichen Eingaben <em>password</em> oder <em>ssh</em>. Der entsprechende Parameter-Abschnitt sei dem Leser zur Übung überlassen...

Die hier verwendete Template Funktion <code>equals</code> ergibt True, wenn beide Argumente gleich sind (wer hätte das gedacht?), ansonsten False. Wenn wir also den Parameter <code>sshOrPW</code> mit "password" belegen, dann wird nur die erste Ressource ausgebracht, die zweite wird ignoriert. Und umgekehrt natürlich. Total einfach, oder? Aber Achtung: Gerade mit mehreren bedingten Ressourcen wird das schnell unübersichtlich, besonders wenn dann auch noch Abhängigkeiten definiert sind. Zum Glück gibt es noch eine Alternative, und zwar mit verschachtelten Templates.

<h2>Verschachtelte Templates</h2>

Vielleicht hat das der ein oder andere noch nie verwendet, aber man kann tatsächlich mehr als ein Template definieren und dann das eine vom anderen aus aufrufen. Das nennt man dann verschachtelte Templates, und man verwendet dafür den Ressourcentyp  <code>Microsoft.Resources/deployments</code>. Einfach ein Name, ein paar Eigenschaften, und in den Eigenschaften steckt die URI zum nächsten Template drin, <code>templatelink</code>. Mehr dazu <a target="_blank" href="https://docs.microsoft.com/de-de/azure/azure-resource-manager/resource-group-linked-templates" rel="noopener">auf AzureDocs</a>.

[code]
  { 
      &quot;apiVersion&quot;: &quot;2017-05-10&quot;, 
      &quot;name&quot;: &quot;linkedTemplate&quot;, 
      &quot;type&quot;: &quot;Microsoft.Resources/deployments&quot;, 
      &quot;properties&quot;: { 
        &quot;mode&quot;: &quot;incremental&quot;, 
        &quot;templateLink&quot;: {
          &quot;uri&quot;: &quot;[variables('subnetTemplate')]&quot;,
          &quot;contentVersion&quot;: &quot;1.0.0.0&quot;
        }, 
        &quot;parameters&quot;: { 
          ... 
        } 
      } 
  } 
[/code]

Die Idee, um das zu einer bedingten Verzweigung zu machen, ist wieder ganz einfach: Wir rufen Template A oder Template B auf, in Abhängigkeit von einer Variablen oder einem Parameter. Klar? Vielleicht ein Beispiel? Das gibt es auch als Download auf 
<a target="_blank" href="https://github.com/gitralf/templates/tree/master/test-conditional" rel="noopener noreferrer">GitHub</a> oder kann - wie unten gezeigt - auch direkt aufgerufen werden. 

Zuerst brauchen wir einen Parameter, um zu entscheiden, ob wir ein Subnetz anlegen oder nicht:

[code]
{
  &quot;$schema&quot;: &quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&quot;,
  &quot;contentVersion&quot;: &quot;1.0.0.0&quot;,
  &quot;parameters&quot;: {
      &quot;subnetYesNo&quot;:{
          &quot;type&quot;: &quot;string&quot;,
          &quot;defaultValue&quot;: &quot;Yes&quot;,
          &quot;allowedValues&quot;: [
              &quot;Yes&quot;,
              &quot;No&quot;
          ]
      }
  },
[/code]

Dann brauchen wir die Links zu den alternativen Templates, und speichern das in einer Variable. Wir definieren auch gleich noch den passenden Namen für das Sub-Deployment, das macht das Debugging übersichtlicher...

[code firstline="15"]
  &quot;variables&quot;: {
    &quot;subnetTemplate&quot;: &quot;[concat('https://raw.githubusercontent.com/gitralf/templates/master/test-conditional/deploysubnet-',toLower(parameters('subnetYesNo')),'.json')]&quot;,
    &quot;subnetTemplateName&quot;: &quot;[concat('Subnet-',parameters('subnetYesNo'))]&quot;,
  },
[/code]

Die entscheidende Stelle nochmal in Großaufnahme:
[code gutter="false"]
&quot;[concat('https://.../test-conditional/deploysubnet-',toLower(parameters('subnetYesNo')),'.json')]&quot;
[/code]

Wir verwenden hier die Funktionen <em>concat</em> und <em>toLower</em>. Ich denke, jeder weiß, was die tun (oder schaut <a target="_blank" href="https://docs.microsoft.com/de-de/azure/azure-resource-manager/resource-group-template-functions-string?toc=%2fazure%2ftemplates%2ftoc.json&amp;bc=%2Fazure%2Ftemplates%2Fbreadcrumb%2Ftoc.json" rel="noopener">hier</a> nach). Wir bauen also den Dateinamen auf, indem wir den übergebenen Parameter integrieren. Wir könnten auch vorher eine entsprechende Variable zusammenbauen und dann hier verwenden. Abhängig von der Eingabe rufen wir jetzt also das Template <code>subnetTemplate</code> auf, das entweder auf <em>deploysubnet-yes.json</em> oder <em>deploysubnet-no.json</em> endet:

[code firstline="19"]
  &quot;resources&quot;: [
    {
      &quot;type&quot;: &quot;Microsoft.Resources/deployments&quot;,
      &quot;apiVersion&quot;: &quot;2015-01-01&quot;,
      &quot;name&quot;: &quot;[variables('subnetTemplateName')]&quot;,
      &quot;properties&quot;: {
        &quot;mode&quot;: &quot;Incremental&quot;,
        &quot;templateLink&quot;: { &quot;uri&quot;: &quot;[variables('subnetTemplate')]&quot; },
      }
    }
  ],
  &quot;outputs&quot;: {
  }
}
[/code]

Für dieses einfache Beispiel verwenden wir zwei identische (leere) Templates (mit den oben zusammengebauten Namen) und starten das Deployment mit dem <em>-Verbose</em> Switch. Wir sehen so was ähnliches wie das hier:

[code gutter="false"]
PS C:\temp&gt; New-AzureRmResourceGroupDeployment -Name &quot;conditional02&quot; -ResourceGroupName vstest1-mcd -verbose -subnetYesNo No -TemplateUri &quot;https://raw.githubusercontent.com/gitralf/templates/master/test-conditional/azuredeploy.json&quot;

AUSFÜHRLICH: Ausführen des Vorgangs &quot;Creating Deployment&quot; für das Ziel &quot;vstest1-mcd&quot;.
AUSFÜHRLICH: 14:37:04 - Template is valid.
AUSFÜHRLICH: 14:37:05 - Create template deployment 'conditional02'
AUSFÜHRLICH: 14:37:05 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 14:37:10 - Resource Microsoft.Resources/deployments 'Subnet-No' provisioning status is running
AUSFÜHRLICH: 14:37:10 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 14:37:15 - Resource Microsoft.Resources/deployments 'Subnet-No' provisioning status is succeeded

DeploymentName          : conditional02
ResourceGroupName       : vstest1-mcd
ProvisioningState       : Succeeded
Timestamp               : 10.08.2017 12:37:14
Mode                    : Incremental
TemplateLink            :
Parameters              :
                          Name             Type                       Value
                          ===============  =========================  ==========
                          subnetYesNo      String                     No

Outputs                 :
DeploymentDebugLogLevel :
[/code]

Es wurde also wie zu sehen ist das zweite Deployment "Subnet-No" aufgerufen, wie erwartet.

<h2>Ausblick</h2>

Es gibt noch andere Wege, um zu entscheiden, ob ein Template aufgerufen werden soll oder nicht. Zum Beispiel kann man einen Array füllen mit den alternativen URLs, und dann etwas Mathe-Magie nutzen, um einen Index zu ermitteln, und übergibt dann das entsprechende Array-Element (wie in <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/15/bedingte-verzweigungen-teil-2/">meinem nächsten Artikel </a>beschrieben). Der Grundgedanke ist aber immer gleich: Man ruft über ein verschachteltes Template weitere Deployments mit verschiedenen URLs auf.

Für heute genug. Aber da werden noch ein paar weitere Artikel zu Azure Resource Manager Templates folgen. Dranbleiben!

Warum nicht in der Zwischenzeit mit einem kostenlosen Azure Testaccount anfangen? Hier der Link <a target="_blank" href="https://azure.microsoft.com/de-de/free/germany/?ralfwi" rel="noopener">für die Microsoft Cloud Deutschland</a>...]]></content:encoded>
    </item>
    <item>
      <title>if...then in ARM Templates</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/if-then-in-arm-templates</link>
      <pubDate>Thu, 10 Aug 2017 15:35:02 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=605</guid>
      <description><![CDATA[(auch verfügbar in Deutsch)

When working with ARM Templates you sometimes hit a point where you...]]></description>
      <content:encoded><![CDATA[(auch verfügbar <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/10/bedingte-verzweigungen-mit-arm-templates/">in Deutsch</a>)

When working with ARM Templates you sometimes hit a point where you wish to have something like if...then, or, in other words, conditional deployments. Well, actually there are ways to do this, and here is how
<!--more-->
<h2>Condition</h2>
The probably easiest way is to use the <code>condition</code> element for a resource. When you describe a resource in your JSON template file, simply place it somewhere inside the resource definition, like here:

[code]
      &quot;condition&quot;: &quot;&lt;true-or-false&gt;&quot;,
      &quot;type&quot;: &quot;&lt;rpnamespace/type&gt;&quot;,
      &quot;apiVersion&quot;: &quot;&lt;api-version&gt;&quot;,
...
[/code]

It is as simple as it looks: Whenever the expression after the condition-element expands to true, the resource defined in this block will be deployed (well, if the rest of the template syntax is correct etc...), or not deployed. Here is an often used example:

[code]
    &quot;condition&quot;: &quot;[equals(parameters('sshOrPW'),'password')]&quot;,
    &quot;apiVersion&quot;: &quot;2016-03-30&quot;,
    &quot;type&quot;: &quot;Microsoft.Compute/virtualMachines&quot;,
    ...
},
{
    &quot;condition&quot;: &quot;[equals(parameters('sshOrPW'),'ssh')]&quot;,
    &quot;apiVersion&quot;: &quot;2016-03-30&quot;,
    &quot;type&quot;: &quot;Microsoft.Compute/virtualMachines&quot;,
    ...
},
[/code]

All we need now is a parameter named <code>sshOrPW</code> with two possible values. The correct parameter section is left as an exercise to the reader...

We are using here a template function, <code>equals</code>. This function expands to True if both arguments are equal (whow, who would have thought that?), otherwise to False. So when we fill the parameter <code>sshOrPW</code> with "password", only the first resource is deployed, the second is ignored. And the other way round, of course. Pretty easy, or? But be warned: Watch out if you have more then one conditionally deployed resource, especially when there is a dependency between. It might be hard to track your template design. Luckily, there is a second way, especially when you have a set of resources to be deployed depending on a condition, and that is with nested templates.

<h2>Nested templates</h2>

You might have never used it, but you can use more then one template and call them from within another template. That's called nested templates, and uses the resource type <code>Microsoft.Resources/deployments</code>. You define a name and some properties, and within the properties there is the link to the other template, <code>templatelink</code>. Find more about this <a target="_blank" href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-linked-templates" rel="noopener">on AzureDocs</a>.

[code]
  { 
      &quot;apiVersion&quot;: &quot;2017-05-10&quot;, 
      &quot;name&quot;: &quot;linkedTemplate&quot;, 
      &quot;type&quot;: &quot;Microsoft.Resources/deployments&quot;, 
      &quot;properties&quot;: { 
        &quot;mode&quot;: &quot;incremental&quot;, 
        &quot;templateLink&quot;: {
          &quot;uri&quot;: &quot;[variables('subnetTemplate')]&quot;,
          &quot;contentVersion&quot;: &quot;1.0.0.0&quot;
        }, 
        &quot;parameters&quot;: { 
          ... 
        } 
      } 
  } 
[/code]

The idea to make this conditional is to call template A or template B, depending on the value of a variable or parameter. Got it? Let's make an example, again with subnets. You find the templates for download in <a target="_blank" href="https://github.com/gitralf/templates/tree/master/test-conditional" rel="noopener noreferrer">my GitHub repository</a> or use it directly from there (as shown below). First we need a parameter to decide if we should deploy a subnet or not:

[code]
{
  &quot;$schema&quot;: &quot;https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#&quot;,
  &quot;contentVersion&quot;: &quot;1.0.0.0&quot;,
  &quot;parameters&quot;: {
      &quot;subnetYesNo&quot;:{
          &quot;type&quot;: &quot;string&quot;,
          &quot;defaultValue&quot;: &quot;Yes&quot;,
          &quot;allowedValues&quot;: [
              &quot;Yes&quot;,
              &quot;No&quot;
          ]
      }
  },
[/code]

We need the links to the alternative templates, and we store them in a variable. We also name our deployment depending on the parameter input, that looks nicer.

[code firstline="15"]
  &quot;variables&quot;: {
    &quot;subnetTemplate&quot;: &quot;[concat('https://raw.githubusercontent.com/gitralf/templates/master/test-conditional/deploysubnet-',toLower(parameters('subnetYesNo')),'.json')]&quot;,
    &quot;subnetTemplateName&quot;: &quot;[concat('Subnet-',parameters('subnetYesNo'))]&quot;,
  },
[/code]

Here a closer look at the main part:

[code gutter="false"]
&quot;[concat('https://.../test-conditional/deploysubnet-',toLower(parameters('subnetYesNo')),'.json')]&quot;
[/code]

We used the functions <em>concat</em> and <em>toLower</em> here. I'm pretty sure you know what they do (if not, <a target="_blank" href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-string?toc=%2fazure%2ftemplates%2ftoc.json&amp;bc=%2Fazure%2Ftemplates%2Fbreadcrumb%2Ftoc.json" rel="noopener">read here</a>). So we build the template URI by integrating the parameter into the filename. Depending on the input we are now calling a template <code>subnetTemplate</code>, which is either ending in <em>deploysubnet-yes.json</em> or <em>deploysubnet-no.json</em>:

[code firstline="19"]
  &quot;resources&quot;: [
    {
      &quot;type&quot;: &quot;Microsoft.Resources/deployments&quot;,
      &quot;apiVersion&quot;: &quot;2015-01-01&quot;,
      &quot;name&quot;: &quot;[variables('subnetTemplateName')]&quot;,
      &quot;properties&quot;: {
        &quot;mode&quot;: &quot;Incremental&quot;,
        &quot;templateLink&quot;: { &quot;uri&quot;: &quot;[variables('subnetTemplate')]&quot; },
      }
    }
  ],
  &quot;outputs&quot;: {
  }
}
[/code]

For this simple test we create identical (empty) templates with the names created above and start the deployment with the <em>-Verbose</em> commandline switch. We see something like this:

[code gutter="false" highlight="1,2,7"]
PS C:\temp&gt; New-AzureRmResourceGroupDeployment -subnetYesNo No -verbose -Name &quot;conditional02&quot; -ResourceGroupName vstest1-mcd
 -TemplateUri &quot;https://raw.githubusercontent.com/gitralf/templates/master/test-conditional/azuredeploy.json&quot;

AUSFÜHRLICH: Ausführen des Vorgangs &quot;Creating Deployment&quot; für das Ziel &quot;vstest1-mcd&quot;.
AUSFÜHRLICH: 14:37:04 - Template is valid.
AUSFÜHRLICH: 14:37:05 - Create template deployment 'conditional02'
AUSFÜHRLICH: 14:37:05 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 14:37:10 - Resource Microsoft.Resources/deployments 'Subnet-No' provisioning status is running
AUSFÜHRLICH: 14:37:10 - Checking deployment status in 5 seconds
AUSFÜHRLICH: 14:37:15 - Resource Microsoft.Resources/deployments 'Subnet-No' provisioning status is succeeded

DeploymentName          : conditional02
ResourceGroupName       : vstest1-mcd
ProvisioningState       : Succeeded
Timestamp               : 10.08.2017 12:37:14
Mode                    : Incremental
TemplateLink            :
Parameters              :
                          Name             Type                       Value
                          ===============  =========================  ==========
                          subnetYesNo      String                     No
[/code]

As we see, the nested deployment "Subnet-No" was called as expected.

<h2>To be continued...</h2>

There are other ways to decide wether or not to call a template. For example, fill an array with the alternative URIs, and then use some math tricks to calculate the index and use that array element (see <a href="https://blogs.technet.microsoft.com/ralfwi/2017/08/15/another-if-then-in-arm-templates/">my next article</a>). But the basic idea is always the same: call a nested template with different URLs.

Enough for today. But stay tuned for other articles from the world of Azure Resource Manager Templates! Coming soon.

Meanwhile why not signing up for a free trial to Azure? Here is the <a target="_blank" href="https://azure.microsoft.com/en-us/free/germany/?ralfwi" rel="noopener">link to Azure Germany</a>...]]></content:encoded>
    </item>
    <item>
      <title>Anmelden am Azure Resource Manager mit Zertifikat</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/anmelden-am-azure-resource-manager-mit-zertifikat</link>
      <pubDate>Mon, 07 Aug 2017 14:56:24 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=596</guid>
      <description><![CDATA[Im alten Azure Portal gab es ja die Möglichkeit, sich ein Management-Zertifikat herunterzuladen und...]]></description>
      <content:encoded><![CDATA[Im alten Azure Portal gab es ja die Möglichkeit, sich ein Management-Zertifikat herunterzuladen und dieses für Logins zum Beispiel in PowerShell zu nutzen (.publishsettings hieß das Zauberwort). Mit dem Azure Resource Manager und dem neuen Azure Portal ist das nicht mehr möglich, hier kommt eine alternative Anmeldemöglichkeit über einen sogenannten "ServicePrincipal" ins Spiel, und so einen wollen wir hier mal anlegen, um uns künftig auch von PowerShell aus per Zertifikat anmelden zu können.

<!--more-->

Mit dem Azure Resource Manager können wir Benutzer oder Gruppen, oder eben auch Applikationen oder Skripten Rechte an Ressourcen zuweisen. Hierzu müssen wir eine Identität erzeugen und dafür sorgen, dass sich die App mit den Credentials dieser Identität authentifiziert. In unserem Fall wäre die App quasi PowerShell, und die Identität eben der genannte ServicePrincipal.

Das Gute an diesem Prinzip ist, dass wir diesem "ServicePrincipal" (also dem Skript oder der Shell) ganz genau Rechte zuweisen können an Subscriptions, Ressourcengruppen oder Ressourcen, basierend auf RBAC, und auch das schauen wir uns an.

Wir brauchen für unser Vorhaben 3 Zutaten:
<ul>
 	<li>ein Zertifikat</li>
 	<li>eine ApplicationID für den ServicePrincipal, und</li>
 	<li> notwendige Berechtigungen.</li>
</ul>
Fangen wir mal mit ein paar Voraussetzungen an…
<h2>Voraussetzungen</h2>
Applikationsregistrierungen können unter Umständen nur vom Tenant-Admin angelegt werden. Im Portal findet man diese Einstellung wie folgt:
<ul>
 	<li>Azure Active Directory wählen</li>
 	<li>User Settings</li>
 	<li>App Registrations</li>
</ul>
Ist diese Einstellung auf "No", dann kann nur ein Admin eine Applikation registrieren, und wenn wir keiner sind, dann müssen wir betteln gehen (ich mein natürlich, einen Change Request stellen und begründen). Nehmen wir mal an, wir dürfen oder wir sind sogar Admin, dann können wir weitermachen.
<h2>Zutat 1: Das Zertifikat</h2>
Wir brauchen ein Zertifikat, am einfachsten ein selbstsigniertes, es gehen aber auch offizielle. Auch hier der Einfachheit halber (wir wollen hier ja kein PKI Tutorial draus machen) erzeugen wir ein selbstsigniertes (selfsigned) Zertifikat. Als Profils machen wir das direkt in PowerShell:

[code gutter="false"]
$cert = New-SelfSignedCertificate -CertStoreLocation &quot;cert:\CurrentUser\My&quot; -Subject &quot;CN=myServicePrincipalCert&quot; -KeySpec KeyExchange
$keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData())
[/code]

Wir haben gerade lokal im Zertifikatsspeicher "My" (oder "Eigene Zertifikate") ein Zertifikat samt Schlüssel erstellt. Das können wir uns übrigens auch anschauen, entweder über den Zertifikatsmanager (<code>certmgr.msc</code>) oder wieder über PowerShell:

[code gutter="false"]
get-childitem -Path &quot;Cert:\CurrentUser\My&quot;
[/code]

Unsere erste Zutat ist damit erledigt. Weiter geht's:
<h2>Zutat 2: Eine ApplicationID</h2>
Wie gelernt brauchen wir eine Applikationsregistrierung. Das können wir entweder via Portal oder auch wieder in der PowerShell machen:

[code gutter="false"]
$sp = New-AzureRMADServicePrincipal -DisplayName &quot;myServicePrincipal&quot; -CertValue $keyValue -EndDate $cert.NotAfter -StartDate $cert.NotBefore
[/code]

Das verwendet Variablen, die wir beim Anlegen des Zertifikats benutzt haben. Falls ein schon vorhandenes Zertifikat verwendet werden soll, dann müssen die Werte entsprechend ausgelesen werden. Als DisplayName bitte irgendwas Aussagekräftigeres wählen als ich hier, eventuell werden das im Laufe der Zeit ein paar mehr ServicePrincipals, und dann findet man die zum Beispiel wieder mittels:

[code gutter="false"] 
Get-AzureADServicePrincipal -SearchString &quot;myServicePrin&quot;
[/code]

Um Rechte zuweisen zu können, benötigen wir später die ApplicationID. Die bekommen wir mit:

[code gutter="false"]
$appID=$sp.ApplicationID
[/code]

<h2>Zutat 3: Berechtigungen</h2>
Erstmal hat die Applikation bzw. der ServicePrincipal keine Rechte. Aber das können wir ändern, natürlich immer vorausgesetzt, wir selber haben die notwendigen Rechte… So detailliert müssen wir RBAC (Role Bases Access Control) hier nicht betrachten, da gibt es <a href="https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles">eine prima Doku dazu</a>. Wir nehmen uns einfach die aktuelle SubscriptionID (und die Tenant-ID können wir uns auch bei der Gelegenheit gleich merken, die brauchen wir unten noch für's Login).

[code gutter="false"]
Get-AzureRmContext
[/code]

Noch ein kleiner Hinweis: Zwischen dem Anlegen des ServicePrincipals und der Sichtbarkeit des Selbigen im AAD können ein paar Sekunden vergehen, also lieber mal ne Minute oder so warten. Dann aber geht's ab:

[code gutter="false"]
New-AzureRmRoleAssignment -Scope &quot;/subscriptions/&lt;sub-id&gt;&quot; -RoleDefinitionName Contributor -ServicePrincipalName $appID
[/code]

So. Jetzt hat die Applikation bzw. der ServicePrinzipal "Contributor"-Rechte auf der Subscription. Soll es ein anderer Scope sein, einfach die entsprechende ResourceID verwenden, zum Beispiel für eine Resourcengruppe:

[code gutter="false"]
Get-AzureRmResourceGroup
[/code]

<h2>Login per Zertifikat</h2>
Alle Zutaten wurden gut verrührt und bei 42 Grad gebacken. Dann können wir jetzt probieren. Wir brauchen die folgenden Angaben:
<ul>
 	<li>Den Thumbprint des Zertifikats</li>
 	<li>Die ApplicationID</li>
 	<li>Die TenantID</li>
</ul>
Ok, statt einem Passwort brauchen wir jetzt 3 Angaben, und das auch noch recht komplizierte. Wo liegt jetzt nochmal der Vorteil? Nun, diese Angaben bleiben immer gleich, also kein Problem, die in einem Skript zu verwenden. Niemand, der nicht auch gleichzeitig Zugriff auf das Zertifikat (genauer den privaten Schlüssel) hat, kann sich damit anmelden. In der Umkehrung heißt das auch, dass das Login erstmal nur auf dem Computer möglich ist, auf dem das Zertifikat erzeugt wurde. Nach einem Export (mit private Key) und Import auf einem anderen Computer würde es dann auch dort gehen.

Den Thumbprint bekommen wir ganz einfach:

[code gutter="false"]
 $Thumbprint = (Get-ChildItem cert:\CurrentUser\My\ | Where-Object {$_.Subject -match &quot;myServicePrincipalCert&quot; }).Thumbprint
[/code]

Als Subject verwenden wir hier die Angabe bei der Erstellung, siehe oben bei Zutat 1.

Das Login sieht dann so aus:

[code gutter="false"]
Login-AzureRmAccount -ServicePrincipal -CertificateThumbprint &lt;thumbprint&gt; -ApplicationId &lt;appID&gt; -TenantId &lt;TenantID&gt;
[/code]

Oder mit einem realen Beispiel:

[code gutter="false"]
Login-AzureRmAccount -ServicePrincipal -CertificateThumbprint B007B51B911914E6CA5630137374A08648D02C8F -ApplicationId 919e6448-9c30-4481-ab5f-be29aa9021e4 -TenantId afa16547-9d30-4591-ac6f-be29aa9031e3
[/code]

Zukünftig könnten wir also einfach beim Starten der PowerShell diese Zeile ausführen, und schon wären wir eingeloggt. Ganz ohne Passwort, denn das Zertifikat befindet sich ja in unserem Zertifikatsspeicher, und mit dem Windows-Login wird auch der Zugriff darauf erst freigegeben. Das heißt aber auch, wer das Zertifikat bekommt (mit dem privaten Schlüssel), und sich die restlichen Angaben beschafft, der kann sich solange anmelden, bis wir die Applikationsregistrierung wieder löschen (oder das Zertifikat abgelaufen ist). Also Aufpassen bei einem eventuellen Kopieren des Zertifikats auf andere Computer!

<h2>Microsoft Azure Deutschland</h2>

Für Azure Deutschland steht das alte Portal ja nicht mehr zur Verfügung, der hier beschriebene Weg ist damit der einzige (und auch generell der empfohlene) Weg, um sich mit Zertifikat anzumelden. Allerdings benötigt das Login noch zusätzlich die Angabe des Environments. Vollständig sieht das also so aus:

[code gutter="false"]
Login-AzureRmAccount -EnvironmentName &quot;AzureGermanCloud&quot; -ServicePrincipal -CertificateThumbprint &lt;thumbprint&gt; -ApplicationId &lt;appID&gt; -TenantId  &lt;TenantID&gt;
[/code]

Für Azure Deutschland gibt es übrigens <a href="https://azure.microsoft.com/de-de/free/germany/?ralfwi">hier ein kostenloses Testaccount…</a>

Eine ausführlichere Beschreibung der Möglichkeiten gibt es übrigens bei <a href="https://docs.microsoft.com/de-de/azure/azure-resource-manager/resource-group-authenticate-service-principal" target="_blank">AzureDocs</a>.]]></content:encoded>
    </item>
    <item>
      <title>Azure Quickstart Templates besser nutzen</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/azure-quickstart-templates-besser-nutzen</link>
      <pubDate>Thu, 27 Jul 2017 17:34:07 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=585</guid>
      <description><![CDATA[Wer sich mit dem Azure Resource Manager beschäftigt, der landet früher (hoffentlich) oder später bei...]]></description>
      <content:encoded><![CDATA[Wer sich mit dem Azure Resource Manager beschäftigt, der landet früher (hoffentlich) oder später bei den <a target="_blank" href="https://github.com/Azure/azure-quickstart-templates" rel="noopener noreferrer">Azure Quickstart Templates auf GitHub</a>. Da findet man für ganz viele Ressourcen Beispiele, die man dann an eigene Bedürfnisse anpassen kann. Heute wollen wir uns mal damit beschäftigen, wie man diese Vorlagen noch besser nutzen kann, insbesondere auch (aber nicht nur) mit Blick auf Microsoft Azure Deutschland.

<!--more-->

<h2>Download</h2>
Klar ist die neueste Version immer nur online verfügbar, aber Git bietet ja von Hause aus schon Möglichkeiten, das offline verfügbar zu machen. Also entweder man lädt sich einfach das komplette Repository runter, oder man fork't und sync't lokal. Ist aber nicht Thema hier, daher bitte bei Git nachlesen, wie das mit fork geht. Download ist einfach, da steht rechts oben ein "Clone or Download" und dort kann man mit "Downlaod ZIP" alles einfach runterladen. Aufpassen, ist eine Menge!

<h2>Übersicht</h2>
Ein Problem ist immer wieder, ein Template zu finden, in dem eine bestimmte Ressource verwendet wird. Dafür habe ich mir mal einen PowerShell-Script (was sonst?) geschrieben, der folgendes macht:
<ul>
 	<li>geht alle JSON-Dateien im Dateibaum durch (nach dem Download, siehe oben). Dabei war es mir egal, ob die JSON Datei auch wirklich verwendet wird (azuredeploy.json) oder nicht (metadata.json), ich durchsuche einfach alle.</li>
 	<li>wenn die JSON-Datei einen Ressourceneintrag hat, dann wird der Typ ausgelesen</li>
 	<li>Template-Name und Ressourcentyp wird in eine Exceltabelle geschrieben (mit Link auf das Raw-Format der Github-Version, kann man aber auch umbauen auf den lokalen Pfad (Zeilen sind auskommentiert)</li>
 	<li>am Ende des Scripts kann man mit dem Excel weiterarbeiten (filtern, sortieren, speichern, was auch immer)</li>
</ul>
Nun kann man in der Excel-Tabelle einfach nach einem bestimmten Ressourcetyp suchen und bekommt alle Templates, die ihn verwenden, per Mausklick präsentiert. Praktisch, oder?

Hier der Skript (ja, ich weiß, quick&amp;dirty, wir gehen ihn aber im Anschluß durch):

[code]
$AQSpath=&quot;C:\woauchimmer\azure-quickstart-templates-master&quot;

$xlo = New-Object -comobject Excel.Application
$xlo.Visible = $true
$xlw = $xlo.Workbooks.Add()
$xlw.Worksheets.Item(1).Name = &quot;RP&quot;
$xl1 = $xlo.Worksheets.Item(1)

$xl1.Cells.Item(1,1).value2 = &quot;TemplateName&quot;
$xl1.Cells.Item(1,2).value2 = &quot;ResourceProvider&quot;

write-host &quot; --checking &quot;+$strpath -ForegroundColor &quot;yellow&quot;

$folders=get-childitem -path $AQSpath -directory
$xl1counter=0
$folders| foreach-object {
    $folder=$_.fullname
    $templatename=$_.Name
    $jsons=get-childitem -path $folder -filter &quot;*.json&quot; -recurse
    if ($jsons.count -gt 0){
        $jsons | foreach-object {
            $json=$_
            write-host &quot; ----checking &quot;+$json.fullname -foregroundColor &quot;yellow&quot;
            $template=get-content -Path $json.fullname | ConvertFrom-Json
            $resources=$template.resources
            if ($resources.count -gt 0){
                $resources | foreach-object {
                    $xl1counter++
                    # $text='=HYPERLINK(&quot;file://'+$json.fullname+'&quot;,&quot;'+$templatename+'&quot;)'
                    $text='=HYPERLINK(&quot;https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/'+$templatename+'/'+$json.Name+'&quot;,&quot;'+$templatename+'&quot;)'
                    $xl1.Cells.Item($xl1counter,1).formula = $text
                    $xl1.Cells.Item($xl1counter,2).value2 = $_.type
                }
            }
        }
    }
}
[/code]

Die ersten Zeilen definieren den Pfad zum lokalen Repository, danach wird ein Excel geöffnet und Spaltenüberschriften gesetzt.

Ab Zeile 16 geht der Script dann alle Verzeichnisse durch und sucht (rekursiv) nach JSON-Dateien. Sehr praktisch ist eine der tollen PowerShell-Funktionen, die hier in Zeile 24 verwendet wird, <code>ConvertFrom-Json</code>. Dieses Cmdlet erlaubt anschließend den einfachsten Zugriff auf alle JSON-Knoten (nennt man das so?). Folgerichtig stecken in <code>$resources</code> alle Ressourcen drin, die das Template enthält, und <code>$resources[x].type</code> liefert den Typ der xten Ressource (oder eben innerhalb einer <code>foreach</code>-Schleife). Ich glaub, da schreib ich gleich noch einen Artikel dazu...

Hat ein bisschen gedauert, herauszufinden, warum Excel die Formel nicht wollte (hier Komma als Trennzeichen verwenden im Hyperlink). Entweder Zeile 29 oder Zeile 30 verwenden, je nachdem ob auf die lokale Datei (29 aktiv) oder auf GitHub (30 aktiv) verlinkt werden soll. Die Variante mit beiden Links sei dem Leser als Übungsaufgabe überlassen...
<h2>Anpassen an Microsoft Azure Deutschland</h2>
Damit die Templates auch in Azure Deutschland laufen, sind meistens nur noch wenige Anpassungen zu machen. Hier die wichtigsten.
<h3>Region</h3>
Die wenigsten Templates sollten eine fest eingetragene Region (location) verwenden. Meistens wird hier die gleiche Region wie die zugehörige Ressourcengruppe verwendet und man findet hoffentlich sowas wie:

[code gutter="false"]
&quot;location&quot;: &quot;[resourceGroup().location]&quot;,
[/code]

<h3>Resource Provider</h3>
Ist der Resource Provider in Azure Deutschland verfügbar? Die meisten sind verfügbar, wer es vorher prüfen möchte, der kann alle RPs bzw. Types im Template ganz einfach testen. Anmelden in PowerShell und die Resourcetypen für einen Namespace auflisten:

[code gutter="false"]
Login-AzureRmAccount -Environment AzureGermanCloud
(Get-AzureRmResourceProvider -ProviderNamespace Microsoft.Compute)|Select-Object Resourcetypes
[/code]

Die ganz Mutigen können das ja auch in eine Excel-Tabelle schieben und dann mit SVERWEIS arbeiten... uuhhhh....
<h3>Image</h3>
Insbesondere ältere VM Images sind nicht alle in Azure Deutschland verfügbar (und eventuell auch nicht in beiden Regionen!). Auch hier bringt PowerShell Klarheit:

[code gutter="false"]
#Liste der Image Publisher
Get-AzureRmVMImagePublisher -Location germanycentral | Select-Object PublisherName

#Liste der Offers für einen bestimmten Publisher
Get-AzureRmVMImageOffer -Location germanycentral -PublisherName SUSE | Select-Object Offer

#Liste der Skus für Publisher und Offer
Get-AzureRmVMImageSku -Location germanycentral -PublisherName SUSE -Offer SLES | Select-Object Skus
[/code]

<h3>VM Größen</h3>
Die früher verwendeten (und noch häufig in den Templates hinterlegten) VM Größen wurden zum Teil durch eine neuere Version (v2) ersetzt. Während in Azure beide Größen zur Verfügung stehen, sind auch hier für Azure Deutschland die Älteren weggefallen, d.h. die Templates müssen angepasst werden. Blöderweise hat jeder so seine eigene Vorstellung, wo "vmSize" im Template definiert werden soll. Entweder als Parameter, Variable, oder direkt in der Microsoft.Compute-Ressource (.properties.hardwareProfile.VmSize: "Standard_D2"). Meist reicht das Anfügen von "_v2" ans Ende, also "Standard_D2_v2". Die Liste der VM Größen liefert uns - Überraschung! - wieder PowerShell, und auch hier ist die Angabe der Region wichtig:

[code gutter="false"]
Get-AzureRmVMSize -Location germanycentral
[/code]

<h3>Hardcoded Strings</h3>
In einigen wenigen Templates sind noch feste Strings drin mit falschen Endpunkten ("windows.net" meistens). Das hab ich aber schon mal <a href="https://blogs.technet.microsoft.com/ralfwi/2016/08/19/arm-templates-fur-azure-deutschland-verwenden/">in einem anderen Blog</a> beschrieben.

<strong>Noch Fragen? Fragen!</strong>

Hier geht's zum <a href="https://azure.microsoft.com/de-de/free/germany/?ralfwi">kostenlosen Testabo für Azure Deutschland</a>!

]]></content:encoded>
    </item>
    <item>
      <title>Site-to-Site VPN zwischen Clouds</title>
      <link>https://docs.microsoft.com/archive/blogs/ralfwi/site-to-site-vpn-zwischen-clouds</link>
      <pubDate>Thu, 02 Feb 2017 15:17:49 GMT</pubDate>
      <dc:creator><![CDATA[RalfWi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ralfwi/?p=565</guid>
      <description><![CDATA[(this article is also available in english)

Nachdem Microsoft Azure Deutschland ja eine separate...]]></description>
      <content:encoded><![CDATA[(this article is also available in english)

Nachdem Microsoft Azure Deutschland ja eine separate Instanz der Azure Cloud ist, mit unterschiedlichen Subscriptions, Rechenzentren, und auch ohne Verbindung zum Backbone, stellt sich die Frage, ob und wie man eine Azure Umgebung netztechnisch mit Azure Deutschland verknüpfen kann. Und genau das soll Thema dieses Blogposts sein.
<!--more-->

Um Netze nach und in Azure zu koppeln gibt es grundsätzlich mal 4 Verfahren:
<ul>
 	<li>VNet Peering</li>
 	<li>VNet-to-VNet (V2V)</li>
 	<li>Site-to-Site (S2S)</li>
 	<li>ExpressRoute(s) (nur der Vollständigkeit halber hier, würde aber zu weit führen)</li>
</ul>
Schauen wir uns also die Möglichkeiten mal an...
<h2>VNet-Peering</h2>
Eine Voraussetzung für VNet-Peering ist, dass sich beide VNets in derselben Region befinden. Da dies bei AZure und AZure Deutschland offensichtlich nicht der Fall ist, müssen wir das hier auch nicht weiter betrachten. Wen VNet-Peering trotzdem interessiert, der findet <a target="_blank" href="https://docs.microsoft.com/de-de/azure/virtual-network/virtual-network-peering-overview">hier einen tollen Artikel</a> dazu.
<h2>VNet-to VNet (V2V)</h2>
VNet-toVnet (V2V) ähnelt dem im folgenden Abschnitt beschriebenen S2S, nur liegen hier beide VNets mit ihren Endpunkten in Azure. Man kann also V2V zum Beispiel verwenden, um Netze in unterschiedlichen Azure-Regionen verbinden zu können, selbst wenn diese in unterschiedlichen Subscriptions angelegt sind (dann halt nur via PowerShell, aber wen stört das schon...). Das Aufsetzen ist super einfach und <a target="_blank" href="https://docs.microsoft.com/de-de/azure/vpn-gateway/vpn-gateway-howto-vnet-vnet-resource-manager-portal">hier prima beschrieben</a>, aber leider müssen für V2V beide Subscriptions innerhalb der gleichen Azure Umgebung liegen, also beide in Azure oder beide in Azure Deutschland. Damit scheidet V2V für unsere geplanten Zwecke auch aus...
<h2>Site-to-Site (S2S)</h2>
Normalerweise verwendet man S2S, um zwischen Azure und der lokalen (on-premise) Netzwerkumgebung einen verschlüsselten Tunnel herzustellen. Man vereint beide Netzwerke also zu einem, indem man jeweils ein Gateway definiert und beide mit den Infos versorgt, wo sich der andere Endpunkt befindet und wie man miteinander redet (IPSec Parameter, Adressen etc). Auch hier gibt es eine <a target="_blank" href="https://docs.microsoft.com/de-de/azure/vpn-gateway/vpn-gateway-howto-site-to-site-resource-manager-portal">einfach nachzuvollziehende Anleitung</a>, von der wir hier nur in Stichworten die Reihenfolge der notwendigen Schritte betrachten:
<ol>
 	<li>Virtuelles Netzwerk erstellen</li>
 	<li>Adressraum definieren</li>
 	<li>Subnetze erstellen</li>
 	<li>Gateway-Subnetz erstellen</li>
 	<li>Gateway für VNet erstellen</li>
 	<li>Gateway für lokales Netz erstellen (quasi der Platzhalter für ihr lokales Gerät)</li>
 	<li>lokales VPN-Gerät konfigurieren</li>
 	<li>Verbindung herstellen</li>
</ol>
Um VNets zwischen Azure und Azure Deutschland zu koppeln, bereitet man in beiden Umgebungen alles vor (Schritt 1-5 aus obiger Liste), um das VNet mit einem normalen VPN-Gerät zu verbinden, und gibt dann einfach die beiden Gateways bei der jeweils anderen Konfiguration als Partner an (Schritt 6). Oder anders ausgedrückt: Statt Schritt 7 wird einfach nochmal Schritt 1-6 in der anderen Umgebung durchgeführt. Man tut also quasi so, als ob der andere Endpunkt ein on-premise Gateway ist. Nachdem beide angelegt sind, führt man Schritt 6 in Azure aus und gibt als Adresse die öffentliche IP des Gateways in Azure Deutschland an, und noch einmal Schritt 6 in Azure Deutschland mit Angabe der öffentlichen IP des Azure-Gateways. Und dann direkt noch Schritt 8, und das war's. ALles klar? Nein? ok, dann hier nochmal:
<ul>
 	<li>Schritt 1-5 für Azure durchführen</li>
 	<li>Schritt 1-5 für Azure Deutschland durchführen</li>
 	<li>Schritt 6 in Azure mit Eingabe der öffentlichen IP des Gateways in Azure Deutschland (aus dem vorherigen Schritt)</li>
 	<li>Schritt 6 in Azure Deutschland mit Eingabe der öffentlichen IP Adresse des Gateways in Azure</li>
 	<li>verbinden mit Schritt 8 (und Schritt 7 entfällt)</li>
</ul>
Ab sofort können dann VMs, die man in einem der beiden VNets anlegt, über den IPSec-Tunnel mit ihren IP-Adressen miteinander kommunizieren.
<h2>Caveat</h2>
Zwischen V2V und S2S gibt es Unterschiede bei den Preisen für Netzwerkverkehr. Bitte unbedingt vorher nachlesen!

Der Tunnel selbst führt über das normale Internet, daher sind Ausfälle oder schwankende Bandbreiten nicht auszuschließen, also genau wie bei S2S-Verbindungen zwischen Azure und on-premise.]]></content:encoded>
    </item>
  </channel>
</rss>