AOUT 06

Azure Policy - Héritez un tag de votre resource group

La mise en place des tags dans Azure est très utilisé notamment pour les besoins des FinOps. Il est donc courant d’avoir des tags liés au contexte projet présents sur nos ressources, même si ceux-ci n’ont aucune utilité fonctionnelle comme un tag pour l’autoshutdown de vos VMs.

Microsoft propose une policy qui permet de reporter un tag d’un groupe de ressource à tous les éléments qu’il contient, il s’agit de la policy suivante Append tag and its value from the resource group, voici sa définition :

{
  "properties": {
    "displayName": "Append tag and its value from the resource group",
    "policyType": "BuiltIn",
    "mode": "Indexed",
    "description": "Appends the specified tag with its value from the resource group when any resource which is missing this tag is created or updated. Does not modify the tags of resources created before this policy was applied until those resources are changed.",
    "metadata": {
      "category": "General"
    },
    "parameters": {
      "tagName": {
        "type": "String",
        "metadata": {
          "displayName": "Tag Name",
          "description": "Name of the tag, such as 'environment'"
        }
      }
    },
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "[concat('tags[', parameters('tagName'), ']')]",
            "exists": "false"
          },
          {
            "value": "[resourceGroup().tags[parameters('tagName')]]",
            "exists": "true"
          },
          {
            "value": "[resourceGroup().tags[parameters('tagName')]]",
            "notEquals": ""
          }
        ]
      },
      "then": {
        "effect": "append",
        "details": [
          {
            "field": "[concat('tags[', parameters('tagName'), ']')]",
            "value": "[resourceGroup().tags[parameters('tagName')]]"
          }
        ]
      }
    }
  },
  "id": "/providers/Microsoft.Authorization/policyDefinitions/9ea02ca2-71db-412d-8b00-7c7ca9fcd32d",
  "type": "Microsoft.Authorization/policyDefinitions",
  "name": "9ea02ca2-71db-412d-8b00-7c7ca9fcd32d"
}

Cette policy est très pratique car elle taggue automatiquement toutes vos ressources.

Maintenant imaginons que nous devions changer la valeur du tag au niveau du resource group pour une raison ou une autre. La policy builtin ne change pas les tags existants, mais surtout elle n’alerte pas que le tag assigné à votre ressource n’est pas le bon comme on peut le voir ci-dessous

Dans mon cas de test je créé un groupe de ressource avec le tag tag-demo à la valeur Demo-blog avec la policy built-in correctement associé, puis je crée un storage account. Ce premier a bien le bon tag qui est appliqué. Dès que la policy est évalué, elle indique que mon groupe de ressource est bien Compliant.

Maintenant, je change la valeur de mon tag, et je recréé un storage account, qui cette fois-ci récupère bien la nouvelle valeur de mon tag.

J’ai donc le résultat suivant :

image

Cependant, quand je regarde ma compliance j’ai l’état suivant :

image

Personnellement, ceci ne m’arrange pas, car je ne peux pas détecter qu’il y a une erreur sur ce tag-ci.

J’ai donc décidé de créer ma propre policy pour détecter si le tag est compliant ou non, tout en conservant la fonctionalité d’héritage des tag du groupe de ressource.

Cela aboutit donc à cette policy, comme vous pouvez le voir j’ai fortement repris celle de Microsoft :

{
    "mode": "Indexed",
    "policyRule": {
        "if": {
            "anyOf": [
                {
                    "allOf": [
                        {
                            "field": "[concat('tags[', parameters('tagName'), ']')]",
                            "exists": "false"
                        },
                        {
                            "value": "[resourceGroup().tags[parameters('tagName')]]",
                            "exists": "true"
                        },
                        {
                            "value": "[resourceGroup().tags[parameters('tagName')]]",
                            "notEquals": ""
                        }
                    ]
                },
                {
                    "allOf": [
                        {
                            "field": "[concat('tags[', parameters('tagName'), ']')]",
                            "exists": "true"
                        },
                        {
                            "value": "[resourceGroup().tags[parameters('tagName')]]",
                            "exists": "true"
                        },
                        {
                            "field": "[concat('tags[', parameters('tagName'), ']')]",
                            "notEquals": "[resourceGroup().tags[parameters('tagName')]]"
                        }
                    ]
                }
            ]
        },
        "then": {
            "effect": "append",
            "details": [
                {
                    "field": "[concat('tags[', parameters('tagName'), ']')]",
                    "value": "[resourceGroup().tags[parameters('tagName')]]"
                }
            ]
        }
    },
    "parameters": {
        "tagName": {
            "type": "String",
            "metadata": {
                "displayName": "Tag Name",
                "description": "Name of the tag, such as 'environment'"
            }
        }
    }
}

Si je rejoue le même test que précédemment j’ai désormais le résultat suivant :

Mes différents Storage Account:

image

Ma compliance est maintenant à 50% :

image image

J’ai fait le choix de ne pas avoir un statut à Non Compliant et d’utiliser qu’une seule policy. Par contre si vous souhaitez avoir un statut à Non Compliant le plus simple est d’utiliser la policy Built-In et de vous créez une custom qui vérifie si la valeur des tags est toujours aligné.


JUIL 27

Azure Policy - Forcez l'évaluation de vos policies

Lorsque vous créez vos propres Azure Policy, il peut être fastidieux de les tester, vu que l’évaluation est déclenchée par Azure.

Il est possible depuis un moment de forcer son exécution au scope d’un groupe de ressource ou d’une souscription. Même si dans notre cas, il s’agit plus de forcer sur un resource group de test plus que sur une souscription pour ne pas impacter vos autres policies.

Il est possible de faire cela en powershell via un simple appel REST.

Pour cela il faut utiliser les urls suivantes :

  • Souscription : https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.PolicyInsights/policyStates/latest/triggerEvaluation?api-version=2018-07-01-preview
  • Resource Group : https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{YourRG}/providers/Microsoft.PolicyInsights/policyStates/latest/triggerEvaluation?api-version=2018-07-01-preview
$azContext = Get-AzContext
$azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
$profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($azProfile)
$token = $profileClient.AcquireAccessToken($azContext.Subscription.TenantId)
$authHeader = @{
    'Content-Type'='application/json'
    'Authorization'='Bearer ' + $token.AccessToken
}

$subscriptionId = ""
$resourceGroup = ""

$restUrl = "POST https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.PolicyInsights/policyStates/latest/triggerEvaluation?api-version=2018-07-01-preview"

 Invoke-WebRequest -Uri $restUrl -Method POST -Headers $authHeader

Et vous retrouverez cette trace dans votre Activity Log :

image

Donc plus d’excuse pour aller chercher un café en attendant que la policy se déclenche.


JUIN 03

ARM - Nommez comme vous le souhaitez votre plan à la consommation pour Azure Function

Si vous avez une convention de nommage pour votre compte Azure, je suppose que comme moi ce qui vous agace le plus ce sont les ressources qui se nomment comme elles le souhaitent.

Avec la popularité d’Azure Function, vous avez dû voir apparaitre des Services Plans nommés “WestEuropePlan” ou tout autre. Et bien sachez qu’il est très simple de le nommer comme vous le voulez grâce à l’ARM suivant :

{
    "type": "Microsoft.Web/serverfarms",
    "apiVersion": "2016-09-01",
    "name": "CustomConsoPlan",
    "location": "[resourceGroup().location]",
    "sku": {
        "name": "Y1"
    }
}

Et pour l’utiliser rien de plus simple, il suffit de rajouter la configuration serverFarmId, comme le démontre le template d’Azure Functions suivant

{
    "apiVersion": "2016-08-01",
    "type": "Microsoft.Web/sites",
    "name": "[parameters('functionAppName')]",
    "location": "[resourceGroup().location]",
    "kind": "functionapp",
    "dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms',  parameters('planName'))]",
        "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]"
    ],
    "properties": {
        "clientAffinityEnabled": false,
        "siteConfig": {
            "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('planName'))]",
            "appSettings": [
                {
                    "name": "AzureWebJobsStorage",
                    "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2015-05-01-preview').key1)]"
                },
                {
                    "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
                    "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',parameters('storageName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), '2015-05-01-preview').key1)]"
                },
                {
                    "name": "WEBSITE_CONTENTSHARE",
                    "value": "[variables('contentShareName')]"
                },
                {
                    "name": "FUNCTIONS_EXTENSION_VERSION",
                    "value": "~2"
                },
                {
                    "name": "FUNCTIONS_WORKER_RUNTIME",
                    "value": "dotnet"
                }
            ]
        }
    }
}

Et voilà, simple et efficace !


AVRI 19

Azure - Résoudre les erreurs liées au module Powershell Az

Ce post peut couvrir une grande partie de vos problèmes en Powershell, et pas uniqument ceux liés aux problèmes avec le module Az.

Vous n’êtes pas sans savoir qu’Azure se base sur des API REST, et que le module Powershell Az se base su celles-ci, comme c’est le cas pour la CLI ou pour les managements librairies.

Maintenant, quand on passe à l’action, il peut arriver qu’on ait des erreurs peu parlantes comme celle-ci :

PS F:\> Add-AzKeyVaultManagedStorageAccount -VaultName $keyvaultName -AccountName $storage.Name -AccountReso
urceId $storage.Id -ActiveKeyName key1 -RegenerationPeriod $period

Add-AzKeyVaultManagedStorageAccount : Operation returned an invalid status code 'BadRequest'
At line:1 char:1
+ Add-AzKeyVaultManagedStorageAccount -VaultName $keyvaultName -Acc ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Add-AzKeyVaultManagedStorageAccount], KeyVaultErrorException
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.KeyVault.AddAzureKeyVaultManagedStorageAccount

Alors pas de panique, pour avoir plus d’infos sur l’erreur, vous pouvez mettre l’option -Debug à votre cmdlet comme ci-dessous :

Add-AzKeyVaultManagedStorageAccount -VaultName $keyvaultName -AccountName $storage.Name -AccountReso
urceId $storage.Id -ActiveKeyName key1 -RegenerationPeriod $period -Debug

Je vous passe le résultat qui peut être assez détaillé, et qui peut contenir des informations sensibles, mais à la fin vous avez le plus souvent un joli message d’erreur clair comme celui-ci

Body:
{
  "error": {
    "code": "Forbidden",
    "message": "Key vault service doesn't have proper permissions to access the storage account
  }
}

En règle générale toutes les commandes powershell qui vous renvoient un Bad Request font un appel REST au sein de leur implémentation.


AVRI 15

ARM - DevTestLabs - Spécifier les images de la gallerie autorisées

Lorsque vous mettez en place DevTestLabs en entreprise il est souhaitable d’avoir un moyen d’automatiser la création de celui-ci. Si on regarde du côté des templates ARM fournis par Microsoft : https://github.com/Azure/azure-devtestlab/tree/master/samples/DevTestLabs/QuickStartTemplates Il est possible d’automatiser la création de la plupart des policies.

Maintenant si on regarde le détail pour limiter les images utilisables de la gallerie, nous avons ce template :

{
  "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "newLabName": {
      "type": "string",
      "metadata": {
        "description": "The name of the new lab instance to be created."
      }
    }
  },
  "variables": {
    "allowedImages": "\"{\\\"offer\\\":\\\"CentOS\\\",\\\"publisher\\\":\\\"OpenLogic\\\",\\\"sku\\\":\\\"7.2\\\",\\\"osType\\\":\\\"Linux\\\",\\\"version\\\":\\\"latest\\\"}\",\"{\\\"offer\\\":\\\"Oracle-Linux\\\",\\\"publisher\\\":\\\"Oracle\\\",\\\"sku\\\":\\\"7.2\\\",\\\"osType\\\":\\\"Linux\\\",\\\"version\\\":\\\"latest\\\"}\",\"{\\\"offer\\\":\\\"SQL2016-WS2012R2\\\",\\\"publisher\\\":\\\"MicrosoftSQLServer\\\",\\\"sku\\\":\\\"Enterprise\\\",\\\"osType\\\":\\\"Windows\\\",\\\"version\\\":\\\"latest\\\"}\""
  },
  "resources": [
    {
      "apiVersion": "2018-10-15-preview",
      "name": "[trim(parameters('newLabName'))]",
      "type": "Microsoft.DevTestLab/labs",
      "location": "[resourceGroup().location]",
      "resources": [

Première chose qui choque, c’est que la variable qui liste les images est juste illisible. Quand on creuse un peu, on s’aperçoit que l’API de microsoft attend quelque chose de ce type en entrée:

"allowedImages": "['ImageReference', 'ImageReference']"

Donc une chaine de caractère contenant un tableau de chaine de caractère au format JSON, et cette chaine correspond à l’objet ImageReference des VM dans Azure.

Partant de cela, je décide de créer un fichier paramètre ARM contenant un tableau d’ImageReference comme suit :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "imageReferences": {
            "value": [
                {
                    "offer": "Windows-10",
                    "publisher": "MicrosoftWindowsDesktop",
                    "sku": "rs4-pro",
                    "osType": "Windows",
                    "version": "latest"
                },
                {
                    "offer": "Windows-10",
                    "publisher": "MicrosoftWindowsDesktop",
                    "sku": "rs4-pron",
                    "osType": "Windows",
                    "version": "latest"
                },
                {
                    "offer": "Windows-10",
                    "publisher": "MicrosoftWindowsDesktop",
                    "sku": "rs5-pro",
                    "osType": "Windows",
                    "version": "latest"
                }
            ]
        }
    }
}

Enfin quelque chose de lisible, qui liste 3 machines Windows 10 dans ce cas précis. Maintenant il ne me reste plus qu’à produire ma chaine de caractère, donc dans mon template ARM, j’ai le code suivant:

"variables": {
    "thresholdName": "threshold",
    "copy": [
        {
            "name": "[variables('thresholdName')]",
            "count": "[length(parameters('imageReferences'))]",
            "input": "[string(parameters('imageReferences')[copyIndex(variables('thresholdName'))])]"
        }
    ],
    "thresholdValue": "[string(variables(variables('thresholdName')))]"
}

Dans la propriété input, je transforme chaque ImageReference en chaine de caractère, puis dans la propriété thresholdValue, je transforme mon tableau en chaine de caractère.

Et voilà je peux assigner ce threshold à ma policies comme suit :

{
    "apiVersion": "2018-10-15-preview",
    "name": "[concat('default','/GalleryImage')]",
    "type": "policySets/policies",
    "condition": "[parameters('canDeploy')]",
    "dependsOn": [
        "[resourceId('Microsoft.DevTestLab/labs', parameters('newLabName'))]"
    ],
    "properties": {
        "description": "",
        "factName": "GalleryImage",
        "evaluatorType": "AllowedValuesPolicy",
        "status": "enabled",
        "threshold": "[variables('thresholdValue')]"
    }
}

Et voilà, comme quoi on peut rendre des templates ARM lisibles.