Le blog technique

Toutes les astuces #tech des collaborateurs de PI Services.

#openblogPI

Retrouvez les articles à la une

Powershell/Azure/Orchestrator : Formatage des volumes additionnels

 

Lors de la création d’une machine virtuelle, on ne se contente en général pas d’un seul « disque dur » (vhd/vmdk). Il est d’usage de conserver le volume principal pour le système, et d’ajouter un ou plusieurs volumes pour les données, les sauvegardes…

Lors de la création de la VM, ces volumes y sont bien rattachés mais ils sont dans un état « brut » : non initialisés, ni partitionnés, ni formatés, ni nommés, et ne disposant pas de lettre de lecteur.

Et si ces opérations peuvent s’avérer fastidieuses lorsqu’il s’agit d’un déploiement manuel, elles deviennent inacceptables lorsqu’il s’agit de VM déployées automatiquement via un portail self-service déclenchant un processus d’orchestration : la VM doit être livrée « prête à fonctionner » à l’utilisateur qui en a fait la demande.

Heureusement, le vénérable outil diskpart peut être scripté, automatisant ainsi le procédé.

Le script powershell que je vous propose ci-dessous doit être exécuté en tant qu’administrateur local, dès que la VM est démarrée. Il détecte tous les disques qui ne disposent d’aucune partition, les initialise, les partitionne (une seule partition primaire), les formate, les renomme DATAxxx et leur attribue la première lettre disponible.

$datadisks = get-wmiobject win32_diskdrive | where partitions -eq 0

$i = 1

foreach ($disk in $datadisks) {

$diskID = $disk.index

$diskname = « DATA » + $i

$dpscript = @ »

SELECT DISK $diskID

ATTRIBUTES DISK CLEAR READONLY

CREATE PARTITION PRIMARY

FORMAT fs=ntfs quick label=$diskname

ASSIGN

« @

$i++

$dpscript | diskpart

}

Il ne reste plus qu’à intégrer ce script à votre processus de déploiement, qu’il s’agisse d’une tâche post-installation dans SCVMM ou d’un runbook Orchestrator :

clip_image002

Il s’agit ici d’un déploiement de VM dans Azure ne disposant que d’un compte administrateur local hors domaine. On commence par monter le disque D : de la VM (disque temporaire créé automatiquement) en tant que partage, puis on y crée le script proposé ci-dessus, on l’exécute à l’aide de la commande powershell.exe -ExecutionPolicy ByPass « & «  »D:\diskpart.ps1″ » »  et on termine en démontant le partage réseau.

L’exécution du script ne prend que quelques minutes même avec plusieurs volumes à formater, et il s’adapte automatiquement quel que soit leur nombre.

Powershell/Azure : utiliser un proxy dans un script

 

Certains scripts powershell peuvent nécessiter un accès au Web, comme par exemple ceux utilisés pour piloter un tenant Azure.

Or, la majorité des organisations utilisent aujourd’hui des proxy pour contrôler ces accès à Internet et il est préférable d’éviter autant que possible les exceptions à cette règle, y compris lorsque c’est un script qui a besoin de se connecter.

Certains cmdlet ont en plus une facheuse tendance à ignorer les paramètres de proxy de l’utilisateur qui les exécute (c’est le cas des cmdlet Azure Powershell tels que Login-AzureRmAccount), et il n’est pas toujours possible ou souhaitable de définir un proxy au niveau du système, via la commande netsh winhttp set proxy…

Heureusement, il est possible de spécifier ces paramètres directement au sein du script !

Dans un premier temps, il est nécessaire de spécifier l’adresse du Proxy :

$proxyAddress = « http://1.2.3.4:8080 »

$proxyUri = new-object System.Uri($proxyAddress)

[System.Net.WebRequest]::DefaultWebProxy = new-object System.Net.WebProxy ($proxyUri, $true)

Si le proxy accepte les connexions anonymes, cela peut suffire.

Autrement, il sera nécessaire de spécifier des credentials autorisés à s’y connecter. Dans ce cas, deux possibilité :

– Soit vous ré-utilisez les credentials de l’utilisateur qui exécute le script :

[System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials

– Soit vous utilisez les credentials d’un autre compte  :

$proxyAccountName = « domain\user »

$proxyPassword = ConvertTo-SecureString « PASSWORD » -AsPlainText -Force

$proxyCred = New-Object System.Management.Automation.PSCredential($proxyAccountName, $proxyPassword)

[System.Net.WebRequest]::DefaultWebProxy.Credentials = $proxycred

En plaçant ces quelques lignes avant toute exécution de cmdlet nécessitant un accès au Web, vous ne devriez plus rencontrer de problème!

Azure : Récupérer l’adresse IP d’une machine virtuelle

 

Il peut parfois s’avérer nécessaire dans un script de récupérer l’adresse IP d’une VM Azure alors que celle-ci n’est pas enregistrée dans le DNS.

Dans Azure « classic» (l’ancienne version), cette information était renvoyée directement à l’aide du cmdlet Get-AzureVM :

clip_image002

Malheureusement, pour les VM créées dans Azure Resource Manager (Azure RM, la « nouvelle » version), le cmdlet Get-AzureRmVM fonctionne différemment et ne renvoie plus directement cette information :

clip_image004

Il renvoie par contre une information intéressante : l’ID de la carte réseau associée à la VM, via la propriété NetworkInterfaceIDs :

clip_image006

La fin de cet ID représente le « display name » de la carte réseau, qui va nous permettre de retrouver les propriétés détaillées de cette carte à l’aide du cmdlet Get-AzureRmNetworkInterface :

clip_image008

Dans ces propriétés détaillées, une en particulier nous intéresse : IpConfigurationsText.
Comme son nom l’indique, elle contient le détail de la configuration Ip de la carte réseau, dont la PrivateIpAddress qui nous intéresse :

clip_image010

Afin d’automatiser ce processus, j’ai réalisé une fonction Get-AzureRmVMIP qui prend en entrée le nom de la VM et celui de son Resource Group, et qui renvoie le nom de la carte réseau et son IP :

clip_image012

Fonction dont voici le code source, que vous pouvez réutiliser ou adapter à votre guise dans vos propres scripts.

Function Get-AzureRmVMIP

{ param

(

[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]

[string]

$Name,

[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]

[string]

$ResourceGroupName

)

Process

{

$VM = Get-AzureRmVM -Name $Name -ResourceGroupName $ResourceGroupName

$NICs = get-AzureRmNetworkInterface -name $vm.NetworkInterfaceIDs.split(« / »)[-1] -ResourceGroupName $ResourceGroupName

$VMIPAddresses = @()

foreach ($NIC in $NICs) {

$VMIPAddresses += @{$NIC.Name = $NIC.IpConfigurations.PrivateIpAddress}

}

Return $VMIPAddresses

}

}