Archives par étiquette : Azure

Automatiser Azure Update Management avec Azure Policy

Avoir de nombreuses machines virtuelles dans Azure implique de les gérer au niveau Patch Management. Sur le papier, on a un service nommé « Update Management » dans Azure Automation. Le problème, c’est que c’est nous qui devons associer chaque machine virtuelle à une politique de Patch Management. C’est une tâche rébarbative dont on se passerait bien. L’idéal serait que le propriétaire puisse inscrire lui-même sa machine virtuelle dans une politique de Patch Management sous forme de choix imposé.

 

Utilisation des tags dans le Patch Management des VM

Les tags permettent de gérer les ressources manière industrielle. Nous allons introduire un tag obligatoire sur les objets machines virtuelles nommé UpdatePolicy. Nous allons utiliser Azure Policy pour imposer deux règles :

  • La présence d’un tag UpdatePolicy
  • Les valeurs associées au tag UpdatePolicy doivent correspondre à une liste établie.

 

La première règle permet de m’assurer que toute machine virtuelle nouvellement créée ou mise à jour sera automatiquement associée à une politique de Patch Management. La seconde permet de s’assurer que seules les valeurs autorisées sont utilisées. Puis, nous allons réutiliser ces tags pour inscrire les machines virtuelles dans des Dynamic Groups au sein d’Update Management.

 

Imposer une politique de patching par défaut

Pour imposer une politique de Patching, nous avons besoin de mettre en place une Azure Policy en mode Append qui va vérifier que le tag UpdatePolicy est bien présent sur les ressources de Type VirtualMachines. Si ce n’est pas le cas, nous allons proposer une valeur par défaut. Une Azure Policy, c’est composé de deux parties. La première partie, c’est la règle en elle-même. La Policy en elle-même repose sur une double condition, à la fois sur le type de ressource impliquée (Microsoft.Compute/VirtualMachines) et l’absence d’un tag particulier dont son nom est précisé en paramètre. Si cette condition est remplie, un tag est ajouté avec une valeur par défaut.

clip_image002

 

Jusqu’à maintenant, on voit bien que la règle est générique, il nous manque des paramètres. C’est l’objectif du second fichier référençant les paramètres. Pas grand-chose à dire à son sujet.

clip_image003

 

Ne reste plus qu’à importer notre Policy au niveau du scope de notre souscription avec la commande New-AzPolicyDefinition

[String]$SubscriptionID = (Get-AzContext).Subscription.id

[String]$Scope = « /subscriptions/$SubscriptionID/ »

[String]$PolicyDefinitionFileURI = « https://raw.githubusercontent.com/Benoitsautierecellenza/DemoPolicy/master/Policies/AZ-DEFAULTTAG/AZ-DEFAULTTAG-01-RULE.json »

[String]$PolicyParameterFileURI = « https://raw.githubusercontent.com/Benoitsautierecellenza/DemoPolicy/master/Policies/AZ-DEFAULTTAG/AZ-DEFAULTTAG-01-PARAMETERS.json »

[String]$PolicyName = « DefautltValue4UpdatePolicy »

[String]$PolicyDisplayName = « $PolicyName »

New-AzPolicyDefinition -Name $PolicyName `

-DisplayName $PolicyDisplayName `

-Policy $PolicyDefinitionFileURI `

-Parameter $PolicyParameterFileURI `

-Subscription $SubscriptionID `

-Mode All

clip_image005

 

Notre Azure Policy est maintenant être à être assignée au niveau de la souscription (ou resource group au sein de la souscription) avec la commande New-AzPolicyAssignment.

[String]$UpdatePolicyTagName = « UpdatePolicy »

[String]$DefaultTagValue = « AlwaysReboot »

[String]$PolicyName = « DefautltValue4UpdatePolicy »

[String]$PolicyAssignname = « MGMT01-VM-P1 »

[String]$Scope = « /subscriptions/$SubscriptionID/ »

$PolicyDefinition = Get-AzPolicyDefinition -SubscriptionId $SubscriptionID -Custom | Where-Object {$_.name -eq $PolicyName}

$AssignParameters = @{

‘UpdatePolicyTagName’ = $UpdatePolicyTagName;

‘DefaultTagValue’=$DefaultTagValue

}

New-AzPolicyAssignment -Name $PolicyAssignname `

-PolicyDefinition $PolicyDefinition `

-Scope $Scope `

-PolicyParameterObject $AssignParameters

clip_image007

 

C’est lors de l’assignation qu’on renseigne les valeurs des paramètres que nous allons utiliser. Dans notre contexte, nous avons deux paramètres :

  • UpdatePolicyTagName : UpdatePolicy
  • DefaultTagValue : AlwaysReboot

 

Imposer un choix de politique de Patching

Si on laisse faire les consommateurs, ils peuvent renseigner une valeur pour notre tag UpdatePolicy mais encore faut-il que celle-ci correspondre à une liste de valeur préétablies que l’on pourra reconnaître. Problème, les valeurs des tags sont sensibles à la case, donc « AlwaysReboot » est différent de « Alwaysreboot ». Pour éviter ce type de problème, nous allons utiliser une seconde Azure Policy pour imposer une des valeurs ci-dessous pour notre tag « UpdatePolicy » :

  • AlwaysReboot
  • RebootIfRequired
  • OnlyReboot
  • NeverReboot

 

Notre Azure Policy reprend le même principe que pour la première avec la double condition. La subtilité, c’est que cette fois, nous savons que le tag existe forcément. Nous devons refuser toute mise à jour de celui-ci qui ne contiendrait pas une des valeurs correspondant à cette autorisée (d’où l’usage du « In »). Si les deux conditions sont remplies alors on refuse l’opération.

clip_image009

 

Le fichier de réponse correspond à peu de choses près à la Policy précédente, si ce n’est l’utilisation du type « Array » pour décrire non pas une chaine de caractère mais une liste.

clip_image011

 

Le processus d’importation de notre Azure Policy ne change pas.

[String]$SubscriptionID = (Get-AzContext).Subscription.id

[String]$Scope = « /subscriptions/$SubscriptionID/ »

[String]$PolicyDefinitionFileURI = « https://raw.githubusercontent.com/Benoitsautierecellenza/DemoPolicy/master/Policies/AZ-ALLOWEDTAGVALUES/AZ-ALLOWEDTAGVALUES-02-RULE.json »

[String]$PolicyParameterFileURI = « https://raw.githubusercontent.com/Benoitsautierecellenza/DemoPolicy/master/Policies/AZ-ALLOWEDTAGVALUES/AZ-ALLOWEDTAGVALUES-02-PARAMETERS.json »

[String]$PolicyName = « AllowedTagValues4UpdatePolicy »

[String]$PolicyDisplayName = « $PolicyName »

New-AzPolicyDefinition -Name $PolicyName `

-DisplayName $PolicyDisplayName `

-Policy $PolicyDefinitionFileURI `

-Parameter $PolicyParameterFileURI `

-Subscription $SubscriptionID `

-Mode All

clip_image013

Tout comme son assignation. La seule subtilité à ce niveau concerne le passage de la liste des valeurs autorisées pour notre tag.

[String]$PolicyAssignname = « Assign-$PolicyName »

$UpdatePolicyTagName = « UpdatePolicy »

$AllowedValues = @(« AlwaysReboot », »RebootIfRequired », »OnlyReboot », »NeverReboot »)

$PolicyDefinition = Get-AzPolicyDefinition -SubscriptionId $SubscriptionID | Where-Object {$_.Properties.displayname -eq $PolicyDisplayName}

$AssignParameters = @{

‘UpdatePolicyTagName’ = $UpdatePolicyTagName;

‘UpdatePolicyTagAllowedValues’=($AllowedValues)

}

New-AzPolicyAssignment -Name $PolicyAssignname `

-PolicyDefinition $PolicyDefinition `

-Scope $Scope `

-PolicyParameterObject $AssignParameters

clip_image015

 

Associer nos machines virtuelles à des groupes dynamiques

Maintenant, cela se passe dans Azure Automation. Je pars du principe que la fonctionnalité Update Management a été activée sur l’instance Automation. Nous allons créer des Deployment Schedules pour chaque type d’OS et chaque type de Policy. Finalement, on doit créer huit Deployment Schedules. Vu que c’est toujours les mêmes opérations, j’ai industrialisé avec le scripts CreateDeploymentSchedules.ps1. Je vous laisse le personnaliser mais dans l’ensemble, il fait le job et on obtient le résultat ci-dessous :

clip_image017

 

A ce stade, nous n’avons plus qu’à patienter un peu que les groupes dynamiques se peuplent

clip_image019

 

Conclusion

Félicitation, vous avez un exemple fonctionnel de service Patching as A Service. Le procédé est déclinable pour imposer la sauvegarde, il faudra juste un peu d’huile de coude et de Runbooks pour gérer l’enregistrement dans Azure Backup mais c’est le même principe. Ce sera certainement le thème du prochain billet.

Stocker des clés SAS de Storage Account dans un KeyVault

Voilà un peu plus d’un an, j’avais publié ce billet : Valet Key avec Azure Function App. Depuis ce temps, je l’utilise énormément. Ce n’est que très récemment en passant un peu de temps dans le KeyVault que j’ai découvert la commande PowerShell Set-AzureKeyVaultManagedStorageSasDefinition. Ça a un peu changé mon approche initiale. Avant, mon Azure Function retournait un clés SAS prête à consommer. Maintenant, j’utilise le Key-Valet pour stocker les clés SAS mises à disposition. Le KeyVault peut stocker ce type de secret (pas visible dans le portail à ce jour). On peut donc ainsi mettre à disposition des clés SAS pré-générées à des d’applications (limitées dans le temps évidement). Pour ce billet, on va se contenter de mettre en œuvre la fonctionnalité :

  • Etape n°1 : Initialisation de notre environnement
  • Etape n°2 : Génération du token SAS
  • Etape n°3 : Expérience côté consommateur

 

Etape n°1 : Initialisation de notre environnement

Pour initialiser notre environnement, nous allons commencer par définir quelques variables. La variable $KeyVaultConsumer désigne un utilisateur référencé dans notre Azure AD pour lequel nous allons mettre en place des permissions RBAC (pour accéder au KeyVault) ainsi que des Access-Policies pour accéder au secret mis à disposition.

$KeyVaultConsumer = « keyvaultconsumer@xxxxxxxxxx.xxx »

$random = Get-Random -Minimum 0 -Maximum 999

$AzureRegion = « WestEurope »

$vaultname = « keyvaultlab$random »

$ResourceGroupname = « KeyVaultLab$random »

$storageaccountname = « keyvaultlab$random »

$sastokenname = « sas$random »

$saspermission = « racwdlup »

$Sastokendurationinminutes = 15

$StorageSKUName = ‘Standard_LRS’

$StorageKind = ‘StorageV2’

$UserPrincipal = (Get-AzureRmContext).Account.id

clip_image002

 

Avec ces variables, nous allons pouvoir initialiser notre environnement comprenant un groupe de ressources contenant à la fois un Storage Account et une instance de Key Vault. Jusque-là rien de très nouveau.

New-AzureRmResourceGroup -name $ResourceGroupname -Location $AzureRegion

$StorageAccount = New-AzureRmStorageAccount -ResourceGroupName $ResourceGroupname -Name $storageaccountname `

-Location $AzureRegion `

-Kind $StorageKind `

-SkuName $StorageSKUName

$Keyvault = New-AzureRmKeyVault -ResourceGroupName $ResourceGroupname -Name $vaultname -Location $AzureRegion -Sku Standard

clip_image004

A ce jour, le portail Azure ne permet pas encore de gérer les permissions sur le Storage. On va donc commencer par se positionner des permissions pour ce type de secret puis référencer notre Storage Account comme « ManagedStorageAccount » auprès de notre instance KeyVault avec la commande Add-AzureKeyVaultManagedStorageAccount

Set-AzureRmKeyVaultAccessPolicy -ResourceGroupName $ResourceGroupname -VaultName $vaultname `

-PermissionsToStorage get,list,delete,set,update,regeneratekey,getsas,listsas,deletesas,setsas,recover,backup,restore,purge `

-UserPrincipalName $UserPrincipal

$keyVault = Get-AzureRmKeyVault -VaultName $vaultname -ResourceGroupName $ResourceGroupname

$Keyvault.AccessPoliciesText

Add-AzureKeyVaultManagedStorageAccount -VaultName $vaultname -AccountName $storageaccountname `

-AccountResourceId $storageaccount.id `

-ActiveKeyName key1 `

-DisableAutoRegenerateKey

$StorageContext = New-AzureStorageContext -StorageAccountName $storageaccountname -Protocol https -StorageAccountKey Key1

clip_image006

Pour ceux qui ont suivi mon billet « Sécuriser ses Storage Accounts dans Azure« , c’est pour l’instant très proche. On peut donc même cumuler les deux fonctionnalités.

 

Etape n°2 : Génération du token SAS

De là, nous pouvons générer notre clé SAS. Par rapport à une approche Valet-Key, le secret n’est pas retourné au demandeur mais directement stocké dans le KeyVault. Attention, ce n’est pas un secret mais un objet de type « ms-sastoken-storage »

$start = [System.DateTime]::Now.AddMinutes(-1)

$end = [System.DateTime]::Now.AddMinutes($Sastokendurationinminutes)

$AccessToken = New-AzureStorageAccountSasToken -Service blob,file,Table,Queue `

-ResourceType Service,Container,Object `

-Permission $saspermission `

-Protocol HttpsOnly `

-StartTime $start `

-ExpiryTime $end `

-Context $StorageContext

$AccessToken

Set-AzureKeyVaultManagedStorageSasDefinition -AccountName $storageaccountname `

-VaultName $vaultname `

-Name $sastokenname `

-TemplateUri $AccessToken `

-SasType ‘account’ `

-ValidityPeriod ([System.Timespan]::FromDays(30))

$secret = Get-AzureKeyVaultSecret -VaultName $vaultname -Name « $vaultname-$sastokenname »

$secret.SecretValueText

$secret.Attributes.ContentType

clip_image008

Pour qu’un consommateur puisse accéder à ce nouveau type de secret (ms-sastoken-storage), il convient de s’assurer qu’il puisse voir l’objet KeyVault dans notre groupe de ressource puis de lui assigner une Access Policy avec uniquement les permissions nécessaires.

New-AzureRmRoleAssignment -SignInName $KeyVaultConsumer -ResourceGroupName $ResourceGroupname -RoleDefinitionName Reader

set-AzureRmKeyVaultAccessPolicy -ResourceGroupName $ResourceGroupname -VaultName $vaultname `

-PermissionsToSecrets Get, List `

-UserPrincipalName $KeyVaultConsumer

Set-AzureRmKeyVaultAccessPolicy -ResourceGroupName $ResourceGroupname -VaultName $vaultname `

-PermissionsToStorage get, list, getsas, listsas -UserPrincipalName $KeyVaultConsumer

$keyVault = Get-AzureRmKeyVault -VaultName $vaultname -ResourceGroupName $ResourceGroupname

$Keyvault.AccessPoliciesText

clip_image010

Etape n°3 : Expérience côté consommateur

Stocker un nouveau type de secret, c’est bien. Maintenant, il faut pouvoir le consommer. Pour la suite de ce billet, je pars du principe que nous sommes connectés avec son identité ($KeyVaultConsumer).

$keyvault = Get-AzurermKeyVault | Out-GridView -Title « Select KeyVault containing SAS Key. » -PassThru

Get-AzureKeyVaultSecret -VaultName $Keyvault.VaultName | Select-Object Name

$secret = Get-AzureKeyVaultSecret -VaultName $Keyvault.VaultName -Name (Get-AzureKeyVaultSecret -VaultName $Keyvault.VaultName).Name

$secret.ContentType

$secret.SecretValueText

clip_image012

 

Maintenant, on peut imaginer des process qui vont automatiquement renouveler des clés pour des consommateurs. Si en plus on combine avec la rotation automatique des clés de Storage Account vu dans mon précédent article, il n’y a plus de raison d’utiliser les clés Full.

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Sécuriser ses Storage Accounts dans Azure

Le stockage Azure est certainement le composant d’Azure le plus ancien. Nous avons actuellement un support Preview pour Azure AD en lieu et place des clés primaires / secondaires. Pourtant, aujourd’hui c’est d’elles que nous allons parler. Chaque Storage Account dispose de deux « Access Key » comme illustré ci-dessous. Notre ami le RSI/RSSI (c’est mieux de l’avoir comme ami que comme ennemi) est très sensible sur les sujets de chiffrement et de condition d’accès au stockage.

clip_image001

 

Il y a quelques bonnes pratiques autour de ces clés. La première est de ne pas les divulguer. A la place, on recommande un mécanisme de type Key-Valet comme illustré dans ce billet : Valet Key avec Azure Function App. La seconde est d’opérer une rotation des clés régulièrement. Notre ami le RSSI/ RSSI rêve d’un bouton rouge qui lui permettrait d’opérer une rotation des clés à loisir. Dans les faits, cela déroule de la manière suivante :

  • On identifie les applications consommant la clé primaire d’un Storage Account donné
  • On reconfigure les applications pour utiliser la clé secondaire
  • On opère une régénération de la clé primaire du Storage Account
  • On reconfigure les applications pour utiliser la nouvelle clé primaire

Voilà pour la théorie. Dans la pratique, c’est un peu plus compliqué que cela. Nous sommes capables de configurer nos applications / services Azure pour utiliser un secret dans un KeyVault, mais il manquait encore une rotation des clés pilotée depuis le KeyVault, c’est le sujet de ce billet.

Mise en place de l’environnement

Pour la mise en place de l’environnement, nous commencerons avec un Storage Account tout ce qu’il y a de plus classique. Sa seule réelle particularité est l’activation de la fonctionnalité Managed Service Instance (MSI). Celle-ci va générer une identité Azure AD qui permettra au Storage Account d’accéder à notre Key Vault pour accéder à sa clé de chiffrement. Au passage, on notera qu’à ce stade, les clés de chiffrement sont celles de Microsoft.

$resourcegroupName = ‘KeyVaultLab’

$StorageAccountname = ‘keyvaultlab994’

$Keyvaultname = ‘keyvaultlab994’

$location = ‘WestEurope’

$StorageSKU = ‘Standard_LRS’

$StorageKind = ‘StorageV2’

$StorageAccountKeyName = ‘key1’

$StorageAccountEncryptionKeyname = $StorageAccountname

New-AzureRmResourceGroup -Name $resourcegroupName -Location $location

$storageaccount = New-AzureRmStorageAccount -ResourceGroupName $resourcegroupName -Name $StorageAccountname -Location $location -SkuName $StorageSKU -Kind $StorageKind -AssignIdentity

$Storageaccount.Encryption

$Storageaccount.Encryption.KeySource

clip_image002

 

Pour compléter l’environnement, nous allons mettre en place une instance du service Key Vault. Celle-ci sera utilisée pour deux usages :

  • Le stockage de notre clé de chiffrement du contenu du KeyVault
  • La mise en place de la rotation automatique des clés
New-AzureRmKeyVault -ResourceGroupName $resourcegroupName –Name $Keyvaultname -Location $location -EnableSoftDelete -EnablePurgeProtection

Add-AzureKeyVaultKey -VaultName $Keyvaultname -Name $StorageAccountEncryptionKeyname -Destination ‘Software’

clip_image003

 

Au passage, on notera un type de permission qui à ce jour n’est pas encore visible dans le portail : Key Vault Managed Storage.

Pour ce Key Vault, nous avons créé un premier secret. Celui-ci sera utilisé pour le chiffrement du contenu du Storage Account avec notre clé.

$KeyvaultObject = Get-AzureRmKeyVault -VaultName $Keyvaultname

$KeyObject = Get-AzureKeyVaultKey -VaultName $Keyvaultname -KeyName $StorageAccountEncryptionKeyname

$KeyObject

clip_image004

 

Pour finir, nous allons mettre en place une « Access policy » au niveau de notre instance du service Key Vault. Cette permission va permet à l’identité Azure AD liée à notre Storage Account d’accéder au Key Vault pour récupérer la clé de chiffrement.

Set-AzureRmKeyVaultAccessPolicy -VaultName $Keyvaultname -ObjectId $Storageaccount.Identity.PrincipalId -PermissionsToKeys wrapKey, unwrapKey, get

(Get-AzureRmKeyVault -VaultName $Keyvaultname).AccessPoliciesText

clip_image005

 

Mise en place de « Customer Managed Key »

La mise en place de la fonctionnalité « Customer Managed Key » permet de ne plus utiliser les clés de chiffrement de Microsoft mais la nôtre, mise à disposition dans clé précédemment mise en œuvre. A ce jour des services comme Azure Table et Azure Queue ne permettent pas encore d’utiliser la fonctionnalité « Customer Managed Key ». Maintenant que notre Storage Account est capable d’accéder aux clés de l’instance Key Vault, nous allons utiliser la commande Set-AzureRmStorageAccount pour préciser les références de la clé de chiffrement à utiliser.

Set-AzureRmStorageAccount -ResourceGroupName $resourcegroupName -Name $StorageAccountname -KeyvaultEncryption -Keyname $KeyObject.Name -KeyVersion $KeyObject.Version -KeyVaultUri $KeyvaultObject.VaultUri

$Storageaccount = Get-AzureRmStorageAccount -ResourceGroupName $resourcegroupName -Name $StorageAccountname

$Storageaccount.Encryption

$Storageaccount.Encryption.KeySource

$Storageaccount.Encryption.KeyVaultProperties

clip_image006

 

Une fois la commande exécutée, on peut constater dans les caractéristiques de notre Storage Account que la clé de chiffrement est maintenant issue du Key Vault, donc sous notre responsabilité. Charge à nous d’en opérer la rotation. Dans le portail Azure, on peut maintenant constater que le paramètre « Encryption » référence bien l’utilisation d’un secret du Key Vault pour assurer le chiffrement des données.

clip_image007

 

Mise en place de la rotation automatique Access Key

C’est la partie la plus intéressante. La première « Access Key » sera stockée dans notre instance du service Key Vault. Techniquement, ce n’est pas l’identité de notre instance Key Vault qui réalisera l’opération mais le Resource Provider « Azure Key Vault ». J’ai passé beaucoup de temps avant de comprendre ce point particulier.

$KeyVaultServicePrincipal = Get-AzureRmADServicePrincipal | Where-Object {$_.displayname -like « Azure Key Vault »}

$KeyVaultServicePrincipal.id.Guid

clip_image008

Pour opérer la rotation des clés, le service Key Vault (au sens générique) doit pouvoir manipuler les Access Key de notre Storage Account. Cela tombe bien, il existe un rôle nommé « Storage Account Key Operator Role » qui remplit parfaitement cet usage. Nous allons assigner ce rôle sur le Storage Account pour l’identité de l’application Azure Key Vault.

Get-AzureRmRoleDefinition -Name ‘Storage Account Key Operator Service Role’

New-AzureRmRoleAssignment -ObjectId $KeyVaultServicePrincipal.id.Guid -RoleDefinitionName ‘Storage Account Key Operator Service Role’ -Scope $Storageaccount.Id

clip_image009

 

On peut constater la permission dans le portail Azure pour l’application « Azure Key Vault ».

clip_image010

 

Prochaine étape, nous allons nous attribuer de nouvelles permissions dans le Key Vault concernant le stockage. Au moment de l’écriture de ce billet, le portail ne permettait pas encore de manipuler ce nouveau type de permission. On va donc réaliser l’opération en PowerShell et ajouter une Access Policy à notre instance du service Key Vault.

Set-AzureRmKeyVaultAccessPolicy -VaultName $Keyvaultname -ResourceGroupName $resourcegroupName -UserPrincipalName (Get-AzureRmContext).account.id -PermissionsToStorage set, get, regeneratekey, list, getsas, setsas

clip_image011

 

Les plus attentifs auront noté les permissions « getsas » et « setsas ». C’est pour le prochain billet, une nouvelle approche du Key-Valet, …

Pour finir, nous allons configurer la rotation automatique de la première « Access Key » de notre Storage Account. L’opération sera réalisée tous les trente jours.

(Get-AzureRmKeyVault -VaultName $Keyvaultname).AccessPoliciesText

$RegenerationPeriod = [System.TimeSpan]::FromDays(30)

$parameters = @{

VaultName = $Keyvaultname

AccountResourceId = $StorageAccount.Id

AccountName = $StorageAccountname

ActiveKeyName = $StorageAccountKeyName

RegenerationPeriod = $RegenerationPeriod

}

Add-AzureKeyVaultManagedStorageAccount @Parameters

clip_image012

Afin de vérifier la bonne mise en place, nous allons opérer une première rotation avec les commandes PowerShell suivantes :

Get-AzureRmStorageAccountKey -ResourceGroupName $ResourceGroupName -Name $storageAccountName

Update-AzureKeyVaultManagedStorageAccountKey -VaultName $keyVaultName -AccountName $storageAccountName -KeyName $StorageAccountKeyName

Get-AzureRmStorageAccountKey -ResourceGroupName $ResourceGroupName -Name $storageAccountName

clip_image013

 

A noter que la rotation n’a été configurée que pour la première « Access Key », pas pour la seconde. Maintenant, vous connaissez la manœuvre.

Quelques notes

  • A ce jour, la fonctionnalité « Customer-Managed keys » ne concerne que les services Blog et File, pas Table et queue qui continuent à utiliser la clé de chiffrement gérée par Microsoft
  • L’utilisation de la fonctionnalité « Soft delete » sur le keyvault a pour conséquence qu’après avoir supprimé un KeyVault, son nom n’est pas réutilisable avant 90 jours, temps nous permettant de le restaurer. Il est vivement recommandé de l’activer.
  • Pour la rotation des clés, le nom de la clé est nécessairement Key1, Key2. N’essayez pas d’utiliser d’autres noms. Conséquence, on ne peut pas avoir tous les « Access Keys » dans un Key Vault unique. Il faut autant de Key Vault que de Storage Accounts

 

Conclusion

Normalement, avec ces deux opérations le RSI/RSSI de votre entreprise devrait apprécier. Il dispose maintenant d’un bouton rouge pour forcer la régénération des clés d’accès au stockage. Pour ne pas casser vos applications / ressources Azure, la prochaine étape sera de les reconfigurer pour extraire l’information du Key Vault et non continuer à coder l’information en dur dans le code ou dans une variable Application Settings dans Web App.

BenoîtS – Simple and secure by design but Business compliant (with disruptive flag enabled)

Générer un certificat auto-signé avec Key-Vault

Pour ceux qui comme moi génèrent des certificats depuis longtemps, je suis passé par toutes les étapes (OpenSSL, New-SelfSignedCertificate). Avec Azure, il était logique que je regarde comment générer un certificat auto-signé. Le problème des solutions citées précédemment, c’était que le certificat était généré localement, dans le magasin personnel de la machine. Combien de fois avez-vous oublié le certificat et sa clé privée sur un serveur ou pire sur votre portable.

Avec Azure, l’usage des certificats s’est banalisé. On associe des Service Principals aux applications déclarées dans Azure AD que l’on consomme ensuite dans différents services (Azure Automation aujourd’hui par exemple). Pour cette raison, j’avais rapidement cherché un moyen de générer mes certificats auto-signés directement dans Azure. Logiquement, j’ai commencé par regarder le KeyVault. Une recherche rapide dans le module PowerShell associé me confirme que c’est bien prévu dans les scénarios du produit.

clip_image001

J’ai donc creusé un peu le sujet et voilà la version courte. On commence par préparer un objet CertificatePolicy :

$AutomationcertificateName = « LabAutomation »

$AutomationcertSubjectName = « cn= » + $AutomationcertificateName

$AutomationCertificateLifetimePolicy = 36

$Policy = New-AzureKeyVaultCertificatePolicy -SecretContentType « application/x-pkcs12 » -SubjectName $AutomationcertSubjectName -IssuerName « Self » -ValidityInMonths $AutomationCertificateLifetimePolicy -ReuseKeyOnRenewal

$Policy

clip_image002

 

Vous l’avez bien compris, on peut personnaliser avec beaucoup d’autres paramètres, mais on va faire court. Pour la suite, cela se passe avec la commande Add-AzureKeyVaultCertificate. Point de détail, la commande retourne un status, à nous de suivre jusqu’à ce que le certificat soit délivré :

$AddAzureKeyVaultCertificateStatus = Add-AzureKeyVaultCertificate -VaultName ‘mykeyvaultforcert’ -Name $AutomationcertificateName -CertificatePolicy $Policy

$AddAzureKeyVaultCertificateStatus.status

While ($AddAzureKeyVaultCertificateStatus.Status -eq « inProgress »)

{

Start-Sleep -Seconds 10

$AddAzureKeyVaultCertificateStatus = Get-AzureKeyVaultCertificateOperation -VaultName ‘mykeyvaultforcert’ -Name $AutomationcertificateName

$AddAzureKeyVaultCertificateStatus.status

}

$AddAzureKeyVaultCertificateStatus

clip_image003

 

Notre certificat auto-signé est maintenant dans le KeyVault, pour l’utiliser, ne nous reste plus qu’à l’exporter. La ça se complique un peu, il faut en passer par un peu de Dot.Net avec la classe X509Certificate2Collection. Dans le code ci-dessous, nous générons un fichier PFX contenant, le certificat, sa clé privée, le tout sécurité par un mot de passe (merci de fermer la session PowerShell après usage !)

$PfxCertPathForRunAsAccount = « C:\TEMP\CERTIFICATE.PFX »

$PfxCertPlainPasswordForRunAsAccount = « P@ssw0rd12345 »

$secretRetrieved = Get-AzureKeyVaultSecret -VaultName ‘mykeyvaultforcert’ -Name $AutomationcertificateName

$pfxBytes = [System.Convert]::FromBase64String($secretRetrieved.SecretValueText)

$certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection

$certCollection.Import($pfxBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)

$protectedCertificateBytes = $certCollection.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12, $PfxCertPlainPasswordForRunAsAccount)

[System.IO.File]::WriteAllBytes($PfxCertPathForRunAsAccount, $protectedCertificateBytes)

Get-ChildItem -Path « c:\temp\cert*.* »

clip_image004

 

Ne reste plus qu’à consommer. Avantage de cette approche, le certificat est préservé dans notre KeyVault. Dans mon contexte, Azure Automation est partie intégrante d’une solution en cours de développement, nous réinstancions donc plusieurs fois par jour nos instances.

 

BenoitS – Simple and Secure by design but Business compliant (with disruptive flag enabled)

Ma session au Powershell Saturday 2018

Ce samedi s’est déroulé l’édition française du PowerShell Saturday dans les locaux de Cellenza. Pendant, cette édition, j’ai eu l’occasion de présenter l’avancement sur Resource Group As a Service. Resource Group As a Service est un sujet que j’avais déjà présenté lors du Global Azure Bootcamp de 2018.

clip_image001

A l’époque, on était plus proche du PoC of Concept, entre temps, le développement a beaucoup avancé. Aujourd’hui, nous sommes maintenant plus proche du Minimum Viable Product. L’objectif de cette session n’était pas de présenter la solution en elle-même mais ce que son développement m’a permis d’apprendre sur Azure Function, Azure Automation et sur PowerShell lui-même. C’est donc plus une session de retour sur expérience.

Pour ceux que cela intéresse, la présentation ainsi que les exemples PowerShell présentés sont disponibles à cette URL.

 

Benoît – Simple and Secure by Design but Business compliant

PoShKeePass un module PowerShell pour KeePass

Je suis un adepte de KeePass depuis longtemps. La solution présente pas mal d’avantages dont d’être certifié par l’ANSSI. Travaillant pour plusieurs clients simultanément, je manipule donc beaucoup de credentials permettant d’accéder aux souscriptions Azure (Avec Multi-Factor authentification cela s’entend). Pour travailler avec le portail pas de problème. Pour les scripts PowerShell, j’ai fini par découvrir le module PowerShell PoShKeePass qui propose un certain nombre de commandes pour manipuler le contenu de notre base de données :

clip_image001

 

En quelques commandes, on peut rapidement accéder à nos secrets :

Import-module PoShKeePass

Get-KeePassDatabaseConfiguration -DatabaseProfileName Personnal

Get-KeePassEntry -DatabaseProfileName Personnal -KeePassEntryGroupPath MyKeePass2018/MyAzure -AsPlainText | format-list

clip_image002

 

Maintenant plus d’excuses pour ne pas sécuriser ses comptes Azure AD à privilèges.

 

Benoît – Simple and secure by design but Business compliant

Cloner des machines virtuelles dans Azure

Avec l’arrivée des Managed Disks, il a été introduit la capacité à cloner ces objets sous forme d’objets Snapshots. Après avoir pas mal joué avec j’y vois plusieurs avantages :

  • Recréer une machine virtuelle à partir d’un snapshot est bien plus rapide que de la restaurer depuis Azure Site Recovery (60Mb/S pour la vitesse).
  • Changer le nom des managed Disks histoire de s’aligner avec notre politique de nommage (genre après une bascule depuis On-Premises avec Azure Site Recovery)
  • Changer le nom de l’objet machine virtuelle (pas le nom du système d’exploitation)
  • Changer la performance des disques utilisés
  • Relocaliser les Managed Disks dans d’autres groupes de ressources (ca ne se déplace pas encore mais on peut cloner)
  • Réaliser une sauvegarde rapide et être en mesure de reconstruire rapidement (updates, opérations de maintenance critiques, mises en production le vendredi, …)
  • Changer l’Availability Zone / Availability Set / région Azure de la machine virtuelle
  • Relocaliser une machine virtuelle sur un autre Virtual network

 

Bref, il y a plein de raisons qui font que cloner une machine virtuelle peut être intéressant. Dans le détail des opérations voilà comment cela se présente :

  • Etape n°1 : Désallouer la machine virtuelle
  • Etape n°2 : Créer des snapshot des managed Disks
  • Etape n°3 : Créer des Managed Disks à partir des snapshots
  • Etape n°4 : Créer une nouvelle machine virtuelle
  • Etape n°5 : Ajouter les disques de données
  • Etape n°6 : Suppression des Snapshots

Etape n°1 : Désallouer la machine virtuelle

Même si l’opération peut être réalisé à chaud, on va quand même désallouer la machine virtuelle histoire de créer une image consistante de tous les Managed Disks. C’est un snapshot de chaque Manage Disks qu’il faudra réaliser. Rappel, désallouer ne veut pas dire shutdown dans le système d’exploitation.

 

Etape n°2 : Créer des snapshot des managed Disks

Nous allons créer autant d’objets Snapshots que de Managed Disks associés à notre machine virtuelle. Le seul disque que l’on ne pourra pas snapshoter. Commencez par identifier les noms Managed Disks associés à votre machine virtuelle. Dans l’exemple ci-dessous ma machine virtuelle comprend un disque OS et un disque de données.

clip_image001

 

Pour chaque Managed Disk, on va utiliser le bouton « Create Snapshot » comme illustré ci-dessous :

clip_image002

 

L’objet Snapshot sera créé dans la même région Azure. Par contre, il est tout à fait possible de créer l’objet dans un Resource Group différent. On peut même change la performance. As-t-on réellement besoin d’avoir notre snapshot en SSD?

clip_image003

Etape n°3 : Créer des Managed Disks à partir des snapshots

Il n’est pas possible de créer une machine virtuelle avec des Snapshots. On doit créer des Managed Disks à partir de nos snapshots.

clip_image004

 

Ici encore, ce qui est intéressant, c’est de pouvoir créer le Managed Disk dans une région Azure, une autre Availability zone. Vu que nous créons l’objet nous pouvons réappliquer notre charte de nommage (ceux qui ont migré des machines virtuelles depuis On-Premises me comprendrons, …).

clip_image005

 

Une fois l’opération terminée, nous devirons avoir autant de Managed Disks que de snapshots comme illustré ci-dessous :

clip_image006

Etape n°4 : Créer une nouvelle machine virtuelle

Avec des disques, nous pouvons créer une nouvelle machine virtuelle. Pour cela nous allons reprendre le Managed Disk contenant notre système d’exploitation. Nous allons utiliser le bouton « Create VM ».

clip_image007

 

Pour la création de la machine virtuelle, il n’y a pas grand-chose à dire.

clip_image008

 

Par contre, à cette étape, il y a un point intéressant. Nous pouvons redéfinir l’Availability zone, voire même l’Availability Set. Il y a même un bonus avec la possibilité de changer à quel Virtual Network la nouvelle carte réseau sera associée.

clip_image009

Etape n°5 : Ajouter les disques de données

N’oubliez pas de redéclarer vos disques de données et de sauvegarder la configuration.

clip_image010

Etape n°6 : Suppression des Snapshots

Conserver les snapshots n’a pas de sens sur le long terme. C’est du stockage facturé, tout comme les Managed Disks. Pensez donc à les supprimer dès que le clonage sera terminé.

clip_image011

 

 

Benoît – Simple and secure by design but Business compliant

Arretons de stocker os tokens Azure avec le module Powershell

Ca faisait quelques temps que j’avais remarqué un comportement « étrange » de certains de mes scripts Azure. Pour beaucoup d’entre eux, je commence par m’assurer avec un Get-AzuremContext que je suis bien authentifié. A ma grande surprise, je découvre que oui, pourtant, je n’ai renseigné aucun credential. Magie ? Nan. J’ai creusé un peu plus le sujet pour redécouvrir le module AzureRM.Profile. Ma recherche m’a amenée sur cette page : Automatic-Context-Autosave avec la découverte de quelques commandes inconnues :

clip_image001

 

Trois commandes ont attiré mon attention :

  • Disable-AzureRmContexteAutoSave
  • Enable-AzurermContextAutosave
  • Get-AzureRmContextAutoSaveSettings

J’ai donc commencé par Get-AzureRmContextAutoSaveSettings avec une surprise. Mes tokens sont bien conservés dans mon profil, c’est la configuration par défaut.

clip_image002

 

En ce qui me concerne, d’un point de vue sécurité, c’est moche. D’une part, je me balade avec les tokens de mes clients (MFA n’est pas encore chez tous et cela ne résout pas toujours le problème). C’est dommage qu’on fasse attention à utiliser des navigateurs en mode privé pour se connecter à Azure si le module PowerShell ignore le même ce concept. D’autre part, cela induit un risque. J’ai tendance à utiliser beaucoup de scripts chez mes différents clients. Avec cette fonctionnalité, je risque de travailler sur la mauvaise souscription sans m’en rendre compte avec un impact tout aussi industriel que le script.

Pour cette raison, j’ai pris l’habitude de désactiver cette fonctionnalité avec la commande Disable-AzureRmContextAutoSave comme illustré ci-dessous :

clip_image003

 

Maintenant, il n’y a plus de risque pour moi.

clip_image004

 

Benoît – Simple and secure by design but Business compliant

Un peu de PowerShell pour gérer les Network Security Groups

De temps en temps, j’ai des clients avec des challenges. Quand on mélange Azure et PowerShell, il y a risque que je réponse présent (même si finalement, je vais me faire mal). Je vous partage donc le challenge du moment. Un de mes clients est en phase de montée en puissance sur Azure avec une exigence, un contrôle strict des flux réseaux entrants et sortants, tout comme il le pratique On-Premises. Tout de suite, ça va faire beaucoup des règles dans les Network Security group. Autant dire tout de suite que le portail ne va pas être d’une grande aide sur le sujet.

Challenge intéressant, challenge Accepté, voici AzureImportNSG.PS1. Ce script PowerShell permet de :

  • Créer l’objet Network Security group (réalise une sauvegarde si existe déjà pour le rollback)
  • Injecter les règles en provenance d’un fichier XML
  • S’assure de la mise en place de la journalisation des flux dans votre instance Log Analytics préférée

clip_image001

Que reste-t-il à faire alors ? Juste lier le Network Security group à l’objet Subnet. Simple and Secure by design ! Et comme il faut pouvoir retravailler les règles, s’il y a un script d’import, il y a aussi un script d’export pour générer notre fichier XML.

clip_image002

Avec cela, plus d’excuse pour ne pas avoir des réseaux en béton. Les deux scripts sont disponibles sur mon repo GitHub.

Benoît – Simple and secure by design but Business compliant

Resource Group as a Service–Mise en place de l’API resourcegroupasaserviceinternalapi

C’est maintenant qu’on commence à rentrer dans les API. Pour commencer nous allons mettre en place l’instance Azure Function qui portera les API à usage interne. J’ai fait le choix d’utiliser le Hosting Plan « App Service Plan » pour des raisons de performance. Cela aura un coût mais mon instance App Service Plan sera disponible 24/24 avec une SKU Standard S1, c’est amplement suffisant. Ce choix permet de disposer d’un service de sauvegarde mais aussi de la fonctionnalité Scale Up. Les logs de cette instance d’Azure Function seront stockés dans le premier Storage account créé. C’est un choix de ma part car lorsqu’on va procéder au renouvellement des clés (primaires / secondaires), avoir trop de dépendances à corriger serait plus que risqué.

clip_image001

Par ce que je veux aller vite, nous allons uploader le contenu de l’API directement dans Azure Function. Pour cela, nous avons besoin d’un peu de FTP. Comme pour une simple WebApp, on peut activer la prise en charge de FTP/FTPS pour charger du contenu, voire même récupérer des logs. J’ai donc reconfiguré le FTP avec un compte et noté le mot de passe dans un coin.

clip_image002

Le contenu que nous allons importer est le fichier resourcegroupasaserviceinternalaip.zip disponible sur mon Github. Une fois récupéré, nous allons utiliser la fonctionnalité Zip Push Deployment pour uploader le contenu via FTP. Derrière, c’est du WebDeploy. Quelques lignes de PowerShell et zou, uploadé dans notre première instance d’Azure Function.

$username = « <Compte FTP> »

$password = « <Mot de passe FTP> »

$filePath = « <emplacement local du fichier resourcegroupasaserviceinternalapi.zip> »

$apiUrl = « « 

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes((« {0}:{1} » -f $username, $password)))

$userAgent = « powershell/1.0 »

Invoke-RestMethod -Uri $apiUrl -Headers @{Authorization=(« Basic {0} » -f $base64AuthInfo)} -UserAgent $userAgent -Method POST -InFile $filePath -ContentType « multipart/form-data »

clip_image003

Dans l’état actuel des choses, mon processus d’importation ne prend pas encore la configuration des Application Settings. En fait, c’est une bonne chose car il faut préserver la configuration déjà en place pour certaines variables essentielles comme AzureWebJobsDashboard ou AzureWebJobStorage). Nous devons juste ajouter les variables ci-dessous documentées ci-dessous :

Nom

Contenu

AuthorizationModuleExpirationPeriod 30
AuthorizationModuleKeyVault resourcegroupasaservice
AuthorizationModuleStorageAccountMasterKey AuthorizationModuleStorageAccountMasterKey
AuthorizationModuleStorageAccountName Resourcegroupasaservice3

 

La variable AuthorizationModuleExpirationPeriod est utilisée pour configurer la durée de vie de la clé du SAS Token qui sera générée par l’API Get-ValetKeyforAzureTableRead. La variable AuthorizationModuleKeyVault désigne le nom de l’instance du service KeyVault qui sera utilisée pour stocker les secrets consommés par les API. La variable AuthorizationModuleStorageAccountName désigne le nom du Storage account qui contient les Azure Tables de configuration. Enfin, la variable AuthorizationModuleStorageAccountMasterKey désigne le nom du secret référençant la clé primaire du stockage désigné par la variable AuthorizationModuleStorageAccountName.

En attendant une industrialisation avec un beau Template ARM, voilà quelques lignes de PowerShell pour configurer nos premières Application Settings. C’est encore artisanal, et donc perfectible.

$webapp = Get-AzureRmWebApp -ResourceGroupName ResourceGroupAsAService -Name resourcegroupasaserviceinternalapi

$AppSettings = @{}

$AppSettings = $webapp.SiteConfig.AppSettings

$hash = @{}

ForEach ($kvp in $AppSettings)

{

$hash[$kvp.Name] = $kvp.Value

}

$hash[‘AuthorizationModuleExpirationPeriod’] = « 30 »

$hash[‘AuthorizationModuleKeyVault’] = « resourcegroupasaservice »

$hash[‘AuthorizationModuleStorageAccountMasterKey’] = « AuthorizationModuleStorageAccountMasterKey »

$hash[‘AuthorizationModuleStorageAccountName’] = « resourcegroupasaservice3 »

Set-AzureRMWebApp -ResourceGroupName resourcegroupasaservice -AppServicePlan resourcegroupasaserviceinternalapi -Name resourcegroupasaserviceinternalapi -AppSettings $hash

clip_image004

Au sein de notre instance Azure Function, notre fonction va devoir accéder aux secrets contenus dans l’instance du service Key Vault dédié à la solution. Pour cela, nous allons utiliser la fonctionnalité Managed Service Identity. Une fois activée, il faut juste penser à ne pas oublier de cliquer sur le bouton Save.

clip_image005

A ce stade, notre application dispose d’une identité dans Azure AD ainsi que d’un Service Principal qui lui est associé. C’est celui-ci que nous allons référencer comme ayant les permissions de parcourir la liste des secrets et accéder à ceux-ci.

$ADDObject = Get-AzureADServicePrincipal | where {$_.displayname -eq « resourcegroupasaserviceinternalapi »}

$ADDObject

Set-AzureRmKeyVaultAccessPolicy -VaultName resourcegroupasaservice -ObjectId $ADDObject.ObjectID -PermissionsToSecrets Get, List

clip_image006

Pour valider que le tout fonctionne bien, nous pouvons appeler notre Azure Function à l’aide du bouton « Run » dans l’éditeur de code d’Azure Function. Normalement, on doit constater qu’un SAS Token nous a été retournée en résultat.

clip_image007

 

Par sécurité, nous devons restreindre l’accès à notre fonction en mettant en imposant l’utilisation d’une Function Key pour utiliser notre fonction et nous allons restreindre les verbes HTTP utilisables à POST uniquement.

clip_image008

Imposer l’utilisation d’une Function Key, c’est aussi en créer une comme illustré ci-dessous.

clip_image009

De retour dans l’éditeur de code de notre Azure Function, en cliquant sur le lien « Get Function URL », on peut retrouver les URL d’accès de notre fonction. Dans la zone de liste, j’ai sélectionné le nom de ma Function Key, ce qui me permet de récupérer l’URL complète.

A partir de maintenant, c’est en utilisant cette URL associée à cette Function Key qu’il sera possible d’appeler l’API Get-ValetKeyforAzureTableRead.

$url = « https://resourcegroupasaserviceinternalapi.azurewebsites.net/api/Get-ValetKeyforAzureTableRead?code=<FunctionKey> »

Invoke-RestMethod -Uri $Url -Method POST -ContentType ‘application/json’


clip_image011

 

En retour, nous avons un SAS Token consommable pour s’authentifier auprès de mon troisième Storage Account. Pour rappel, c’est lui qui contient les Azure Tables que nous avons mis en œuvre.

Voilà pour la mise en place de la première API. La démarche sera sensiblement la même pour la seconde instance d’Azure Function. On va juste ajouter la brique Azure AD pour l’authentification.

BenoîtS – Simple and Secure by Design but Business compliant