SEPT 15

Github Actions - Créer ses étapes personnalisées

Lors de mon dernier article je vous ai présenté comment utiliser Github Action pour déployer vos templates ARM.

Pour cela, j’ai utilisé une action provenant d’un repository Microsoft Azure.

Cette action permet de :

  • Créer un groupe de ressource
  • Déployer un template ARM & un fichier de paramètres depuis une URL relative ou absolue.
  • Supprimer un groupe de ressource.

Maintenant elle a aussi des défauts, à ce jour, on ne peut pas déployer sans fichier de paramètre, et il n’est pas possible de déployer au niveau d’une souscription.

J’ai donc décidé de créer ma propre action disponible sur mon github : Azure Action ARM

Elle permet de déployer soit sur un groupe de ressource soit sur une souscription avec ou non un paramètre.

Pour ce faire, il vous suffit juste de créer une image Docker qui réalisera ce dont vous avez besoin. Pour ma part, j’ai repris quelque chose de similaire de celles qui sont fournis par Microsoft.

A savoir le Dockerfile suivant


FROM microsoft/azure-cli:2.0.47

LABEL version="1.0.0"

LABEL maintainer="Wilfried Woivré"
LABEL com.github.actions.name="Déploy ARM Template"
LABEL com.github.actions.description="GitHub Action to deploy ARM template to Azure"
LABEL com.github.actions.icon="triange"
LABEL com.github.actions.color="blue"

ENV GITHUB_ACTION_NAME="Azure - Deploy ARM Template"

RUN apk update \
  && apk add --no-cache util-linux

COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

Et un fichier bash entrypoint.sh suivant :

#!/bin/bash

set -e
export AZURE_HTTP_USER_AGENT="GITHUBACTIONS_${GITHUB_ACTION_NAME}_${GITHUB_REPOSITORY}"

URI_REGEX="^(http://|https://)\\w+"
GUID=$(uuidgen | cut -d '-' -f 1)

if [[ -z "$SCOPE" ]]
then
  SCOPE="RESOURCE_GROUP"
fi

if [[ $SCOPE = "RESOURCE_GROUP" ]] && [[ -z "$AZURE_RESOURCE_GROUP" ]]
then
  echo "AZURE_RESOURCE_GROUP is not set." >&2
  exit 1
fi

if [[ -z "$AZURE_TEMPLATE_LOCATION" ]]
then
    echo "AZURE_TEMPLATE_FILE is not set." >&2
    exit 1
fi


# Download parameters file if it is a remote URL

if [[ -z "$AZURE_TEMPLATE_PARAM_LOCATION" ]]
then
  echo "No parameter files set"
else
  if [[ $AZURE_TEMPLATE_PARAM_LOCATION =~ $URI_REGEX ]]
  then
    PARAMETERS=$(curl "$AZURE_TEMPLATE_PARAM_LOCATION")
    echo "Downloaded parameters from ${AZURE_TEMPLATE_PARAM_LOCATION}"
  else
    PARAMETERS_FILE="${GITHUB_WORKSPACE}/${AZURE_TEMPLATE_PARAM_LOCATION}"
    if [[ ! -e "$PARAMETERS_FILE" ]]
    then
      echo "Parameters file ${PARAMETERS_FILE} does not exits." >&2
      exit 1
    fi
    PARAMETERS="@${PARAMETERS_FILE}"
  fi
fi

# Generate deployment name if not specified

if [[ -z "$DEPLOYMENT_NAME" ]]
then
  DEPLOYMENT_NAME="Github-Action-ARM-${GUID}"
  echo "Generated Deployment Name ${DEPLOYMENT_NAME}"
fi

# Deploy ARM template

if [[ $SCOPE = 'RESOURCE_GROUP' ]]
then
  if [[ $AZURE_TEMPLATE_LOCATION =~ $URI_REGEX ]]
  then
    if [[ -z "$PARAMETERS" ]]
    then
      az group deployment create -g "$AZURE_RESOURCE_GROUP" --name "$DEPLOYMENT_NAME" --template-uri "$AZURE_TEMPLATE_LOCATION"
    else
      az group deployment create -g "$AZURE_RESOURCE_GROUP" --name "$DEPLOYMENT_NAME" --template-uri "$AZURE_TEMPLATE_LOCATION" --parameters "$PARAMETERS"
    fi
  else
    TEMPLATE_FILE="${GITHUB_WORKSPACE}/${AZURE_TEMPLATE_LOCATION}"
    if [[ ! -e "$TEMPLATE_FILE" ]]
    then
      echo "Template file ${TEMPLATE_FILE} does not exists." >&2
      exit 1
    fi
    if [[ -z "$PARAMETERS" ]]
    then
      az group deployment create -g "$AZURE_RESOURCE_GROUP" --name "$DEPLOYMENT_NAME" --template-file "$AZURE_TEMPLATE_LOCATION"
    else
      az group deployment create -g "$AZURE_RESOURCE_GROUP" --name "$DEPLOYMENT_NAME" --template-file "$AZURE_TEMPLATE_LOCATION" --parameters "$PARAMETERS"
    fi
  fi
fi

if [[ $SCOPE = 'SUBSCRIPTION' ]]
then
  if [[ $AZURE_TEMPLATE_LOCATION =~ $URI_REGEX ]]
  then
    if [[ -z "$PARAMETERS" ]]
    then
      az deployment create --location "$DEPLOYMENT_LOCATION" --name "$DEPLOYMENT_NAME" --template-uri "$AZURE_TEMPLATE_LOCATION"
    else
      az deployment create --location "$DEPLOYMENT_LOCATION" --name "$DEPLOYMENT_NAME" --template-uri "$AZURE_TEMPLATE_LOCATION" --parameters "$PARAMETERS"
    fi
  else
    TEMPLATE_FILE="${GITHUB_WORKSPACE}/${AZURE_TEMPLATE_LOCATION}"
    if [[ ! -e "$TEMPLATE_FILE" ]]
    then
      echo "Template file ${TEMPLATE_FILE} does not exists." >&2
      exit 1
    fi
    if [[ -z "$PARAMETERS" ]]
    then
      az deployment create --location "$DEPLOYMENT_LOCATION" --name "$DEPLOYMENT_NAME" --template-file "$AZURE_TEMPLATE_LOCATION"
    else
      az deployment create --location "$DEPLOYMENT_LOCATION" --name "$DEPLOYMENT_NAME" --template-file "$AZURE_TEMPLATE_LOCATION" --parameters "$PARAMETERS"
    fi
  fi
fi

Pour l’utiliser, il suffit de créer vos étapes de la manière suivantes :

    - name: Azure - Deploy ARM Template - Subscription Level with parameters
      uses: wilfriedwoivre/github-actions/Azure/arm@master
      env:
        SCOPE: SUBSCRIPTION
        DEPLOYMENT_LOCATION: West Europe
        AZURE_TEMPLATE_LOCATION: arm-deployment-sub.json
        AZURE_TEMPLATE_PARAM_LOCATION: arm-deployment-sub.parameters.json

Dans la propriété use on retrouve mon image Docker selon le format suivant : mon_nom_d’utilisateur/mon_repo_github/arborescence@branch

L’image est ensuite construite lors de chaque début de job sur Github Action

Pour aller plus loin, il est possible de publier votre action sur le marketplace de Github : https://developer.github.com/marketplace/actions/publishing-an-action-in-the-github-marketplace/


SEPT 07

ARM - Utiliser Github Actions pour déployer vos templates

Je suppose que vous n’êtes pas sans savoir que Github Action est disponible en beta !!! Et bonne nouvelle pour moi c’est disponible.

Voyons comment déployer un template ARM depuis notre repo github lorsqu’il y a une modification de ce dernier.

Pour commencer, il nous faut :

  • Un repository github (public ou privée)
  • Accès à Github Action
  • Une souscription Azure
    • Un resource group
    • Un SPN avec les accès contributeurs sur notre ressource group

Sur votre repository Github, il faut ajouter les secrets suivants:

  • Tenant Id
  • Application Id
  • Application Secret

Exactement, comme on peut le voir ci-dessous : image

Maintenant plus qu’à écrire notre action:

name: Deploy to Azure Workflow

on:
 push:
    branches:
    - master
    path:
      - arm/*

jobs:
  deploy:
    name: Deploy to Azure
    runs-on: ubuntu-latest
    steps:
    - name: Git - Get Sources
      uses: actions/checkout@master

    - name: Azure - Login
      uses: Azure/github-actions/login@master
      env:
        AZURE_SUBSCRIPTION: MySubName
        AZURE_SERVICE_APP_ID: ${ { secrets.AZURE_SERVICE_APP_ID } }
        AZURE_SERVICE_PASSWORD: ${ { secrets.AZURE_SERVICE_PASSWORD } }
        AZURE_SERVICE_TENANT: ${ { secrets.AZURE_SERVICE_TENANT } }

    - name: Azure - Deploy ARM Template
      uses: Azure/github-actions/arm@master
      env:
        AZURE_RESOURCE_GROUP: github-actions
        AZURE_TEMPLATE_LOCATION: ./arm/sample-arm.deploy.json
        AZURE_TEMPLATE_PARAM_LOCATION: ./arm/sample-arm.deploy.parameters.json

Alors si on regarde un petit peu plus notre workflow.

On retrouve les éléments suivants :

Le nom de notre workflow

name: Deploy to Azure Workflow

Sur quel évènement il se déclenche, ici il s’agit d’un push sur la branche master qui modifie un fichier dans le dossier arm

on:
 push:
    branches:
    - master
    path:
      - arm/*

Puis la définition de notre job, à savoir son nom et où est-c que celui s’éxécute.

jobs:
  deploy:
    name: Deploy to Azure
    runs-on: ubuntu-latest

A ce jour, les valeurs disponibles pour faire tourner notre actions sont les suivantes Github Action documentation:

  • ubuntu-latest, ubuntu-18.04, or ubuntu-16.04
  • windows-latest, windows-2019, or windows-2016
  • macOS-latest or macOS-10.14

Maintenant, passons à nos différentes étapes, la première est la plus simple il s’agit de récupérer le code source

    steps:
    - name: Git - Get Sources
      uses: actions/checkout@master

Commençons par nous connecter à nous connecter à Azure grâce à notre SPN. Ici, nous allons utiliser une action disponible sur le repo Git Azure Action

    - name: Azure - Login
      uses: Azure/github-actions/login@master
      env:
        AZURE_SUBSCRIPTION: MySubName
        AZURE_SERVICE_APP_ID: ${ { secrets.AZURE_SERVICE_APP_ID } }
        AZURE_SERVICE_PASSWORD: ${ { secrets.AZURE_SERVICE_PASSWORD } }
        AZURE_SERVICE_TENANT: ${ { secrets.AZURE_SERVICE_TENANT } }  

Et pour finir, voici notre déploiement :

    - name: Azure - Deploy ARM Template
      uses: Azure/github-actions/arm@master
      env:
        AZURE_RESOURCE_GROUP: github-actions
        AZURE_TEMPLATE_LOCATION: ./arm/sample-arm.deploy.json
        AZURE_TEMPLATE_PARAM_LOCATION: ./arm/sample-arm.deploy.parameters.json

On peut par ailleurs retrouver notre workflow sur Github :

image

En tout cas, voici une raison de plus d’utiliser Github pour vos projets.


SEPT 03

Azure Policy - Mettre en place l'arrêt automatique de vos VMs

Lorsqu’on parle de Cloud, on parle très souvent des coûts que cela implique. Il est nécessaire d’optimiser son budget pour qu’à la fin du mois la facture ne soit pas trop salée. Il existe plusieurs solutions pour que les finances restent au vert à la fin du mois comme les suivantes :

  • Supprimer les ressources inutilisées…. Mais qui a laissé trainer cet Azure Firewall
  • Eteindre vos VMs le soir sur vos environnements hors production
  • Mettre en place de l’autoscaling sur les environnements de production
  • Créer des environnements temporaires pour vos tests. N’oubliez pas que je vous ai fournis tous les outils pour réaliser votre propre Sandbox

Revenons sur le point d’éteindre vos machines le soir, à part s’il s’agit de votre carte bleue personnelle, vous l’oubliez généralement un soir sur deux, si ce n’est plus. Mais il est possible d’automatiser cela de plusieurs manières :

  • Auto-shutdown: Disponible de manière built in dans Azure, via la blade de votre VM, il y a l’interface pour l’AutoShutdown. On retrouvera ici à configurer une heure d’arrête sur une timezone spécifique, et la possibilité d’avoir une notification avant l’extinction de la VM. Il est bien entendu possible d’activer ou désactiver cette fonctionnalité par VM.
    • Avantages: Possibilité de configurer de manière granulaire chacune de vos VMS, et de définir pour chacune un horaire spécifique

    • Inconvénients : Fastidieux à mettre en place pour 300 VMS. Pas possible de redémarrer vos VMS le matin.

  • Azure Automation: Azure Automation offre une solution built-in pour cela. Vous pouvez retrouver toutes les informations dans la docs Microsoft
    • Avantages : Toutes vos VMS sont configuréees pour s’éteindre et redémmarrer. Sauf celles que vous avez exclues

    • Inconvénients : Même horaire pour toutes vos VMs d’une souscription, ou alors il faut faire plusieurs automations.

  • Azure Automation & Tags: Il est possible de créer un runbook qui allume ou éteint vos serveurs en fonction de tags. Il est possible d’en trouver dans la galerie
    • Avantages : Possibilité de gérer chacune de vos VMS de manière indépendante via des tags. La gestion du script est centralisé sur un seul Azure Automation.

    • Inconvénients : Runbook à gérer et à monitorer en cas d’erreur.

Dans mon cas, j’ai le besoin d’éteindre mes VMs à une heure précise (par VM) et un démarrage manuel, puisque mon planning change tous les jours. Il faut donc que je mette en place l’autoshutdown par VM.

Maintenant pour éviter le côté fastidieux, il est possible de créer un template ARM pour déployer notre ressource :

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "vmName": {
            "type": "string"
        },
        "location": {
            "type": "string"
        },
        "status": {
            "type": "string",
            "allowedValues": [
                "Enabled",
                "Disabled"
            ]
        },
        "shutdownHour": {
            "type": "string"
        },
        "timeZone": {
            "type": "string"
        }
    },
    "variables": {
        "shutdownHour": "[replace(parameters('shutdownHour'), ':', '')]"
    },
    "resources": [
        {
            "type": "Microsoft.DevTestLab/schedules",
            "apiVersion": "2016-05-15",
            "name": "[concat('shutdown-computevm-', parameters('vmName'))]",
            "location": "[parameters('location')]",
            "properties": {
                "status": "[parameters('status')]",
                "taskType": "ComputeVmShutdownTask",
                "dailyRecurrence": {
                    "time": "[variables('shutdownHour')]"
                },
                "timeZoneId": "[parameters('timeZone')]",
                "notificationSettings": {
                    "status": "Disabled",
                    "timeInMinutes": 30
                },
                "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]"
            }
        }
    ],
    "outputs": {
        "policy": {
            "type": "string",
            "value": "[concat('Autoshutdown configured for VM', parameters('vmName'))]"
        }
    }
}

Il est maintenant possible de créer cet objet pour chaque VM via une Azure Policy avec un effet deployIfNotExists

Pour le passage de mes paramètres à mon template ARM, je décide de mettre des tags sur mes VMs, et de les utiliser en paramètre comme le montre la policy ci-dessous :

{
  "mode": "Indexed",
  "policyRule": {
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.Compute/virtualMachines"
        },
        {
          "field": "[concat('tags[', parameters('tagAutoShutdownEnabled'), ']')]",
          "exists": "true"
        },
        {
          "field": "[concat('tags[', parameters('tagAutoShutdownHour'), ']')]",
          "exists": "true"
        },
        {
          "field": "[concat('tags[', parameters('tagAutoShutdownTimeZone'), ']')]",
          "exists": "true"
        }
      ]
    },
    "then": {
      "effect": "deployIfNotExists",
      "details": {
        "type": "Microsoft.DevTestLab/schedules",
        "name": "[concat('shutdown-computevm-', field('name'))]",
        "existenceCondition": {
          "allOf": [
            {
              "field": "tags.AutoShutdown-Enabled",
              "equals": "[field('tags.AutoShutdown-Enabled')]"
            },
            {
              "field": "tags.AutoShutdown-Hour",
              "equals": "[field('tags.AutoShutdown-Hour')]"
            },
            {
              "field": "tags.AutoShutdown-TimeZone",
              "equals": "[field('tags.AutoShutdown-TimeZone')]"
            }
          ]
        },
        "roleDefinitionIds": [
          "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
        ],
        "deployment": {
          "properties": {
            "mode": "incremental",
            "template": {
              "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
              "contentVersion": "1.0.0.0",
              "parameters": {
                "vmName": {
                  "type": "string"
                },
                "location": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "allowedValues": [
                    "Enabled",
                    "Disabled"
                  ]
                },
                "shutdownHour": {
                  "type": "string"
                },
                "timeZone": {
                  "type": "string"
                }
              },
              "variables": {
                "shutdownHour": "[replace(parameters('shutdownHour'), ':', '')]"
              },
              "resources": [
                {
                  "type": "Microsoft.DevTestLab/schedules",
                  "apiVersion": "2016-05-15",
                  "name": "[concat('shutdown-computevm-', parameters('vmName'))]",
                  "location": "[parameters('location')]",
                  "tags": {
                    "AutoShutdown-Enabled": "[parameters('status')]",
                    "AutoShutdown-TimeZone": "[parameters('timeZone')]",
                    "AutoShutdown-Hour": "[parameters('shutdownHour')]"
                  },
                  "properties": {
                    "status": "[parameters('status')]",
                    "taskType": "ComputeVmShutdownTask",
                    "dailyRecurrence": {
                      "time": "[variables('shutdownHour')]"
                    },
                    "timeZoneId": "[parameters('timeZone')]",
                    "notificationSettings": {
                      "status": "Disabled",
                      "timeInMinutes": 30
                    },
                    "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('vmName'))]"
                  }
                }
              ],
              "outputs": {
                "policy": {
                  "type": "string",
                  "value": "[concat('Autoshutdown configured for VM', parameters('vmName'))]"
                }
              }
            },
            "parameters": {
              "vmName": {
                "value": "[field('name')]"
              },
              "location": {
                "value": "[field('location')]"
              },
              "status": {
                "value": "[field('tags.AutoShutdown-Enabled')]"
              },
              "shutdownHour": {
                "value": "[field('tags.AutoShutdown-Hour')]"
              },
              "timeZone": {
                "value": "[field('tags.AutoShutdown-TimeZone')]"
              }
            }
          }
        }
      }
    }
  },
  "parameters": {
    "tagAutoShutdownEnabled": {
      "type": "String",
      "metadata": {
        "displayName": "Tag Name",
        "description": null
      },
      "defaultValue": "AutoShutdown-Enabled"
    },
    "tagAutoShutdownTimeZone": {
      "type": "String",
      "metadata": {
        "displayName": "Tag Name",
        "description": null
      },
      "defaultValue": "AutoShutdown-TimeZone"
    },
    "tagAutoShutdownHour": {
      "type": "String",
      "metadata": {
        "displayName": "Tag Name",
        "description": null
      },
      "defaultValue": "AutoShutdown-Hour"
    }
  }
}

Je vous ai fait un Gist pour que ce soit plus simple pour les copier coller Azure Policy Auto Shutdown

Pour ceux qui ont déjà mis en place des policy avec deployIfNotExists, vous n’êtes pas sans savoir qu’elle ne se réapplique pas si elle considère qu’elle n’a pas besoin de le faire.

Pour cela, Azure se base d’abord sur les propriétés type et name

"effect": "deployIfNotExists",
"details": {
    "type": "Microsoft.DevTestLab/schedules",
    "name": "[concat('shutdown-computevm-', field('name'))]",

Puis après sur la propriété existenceCondition qui reprend les mêmes contraintes que les règles dans les policies. Je ne peux donc pas lire les propriétés de mon objet DevTestLab, j’ai donc utilisé des tags sur ma ressource autoshutdown, que je lis et compare à ceux de la VM

"existenceCondition": {
    "allOf": [
    {
        "field": "tags.AutoShutdown-Enabled",
        "equals": "[field('tags.AutoShutdown-Enabled')]"
    },
    {
        "field": "tags.AutoShutdown-Hour",
        "equals": "[field('tags.AutoShutdown-Hour')]"
    },
    {
        "field": "tags.AutoShutdown-TimeZone",
        "equals": "[field('tags.AutoShutdown-TimeZone')]"
    }
    ]
},

Et voilà le tour est joué, j’ai une policy globale sur ma souscription, qui éteint mes VMs selon mes propres critères. Et je peux changer ses valeurs quand je le souhaite !


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 = "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.