Question 2011 Scripting Games : Advanced Event 5

Plus d'informations
il y a 14 ans 10 mois #9627 par Matthew BETTON
Bonsoir,

Ci-après le script que j'ai posté lors des Scripting Games 2011, dans le cadre de l'Advanced Event 5.

Le scénario était le suivant :

Event scenario

Your company is planning an upgrade from Windows XP to Windows 7, and you get to write a script that will determine if a computer meets the requirements for the upgrade. Here are the requirements for the Windows 7 operating system:

1 gigahertz (GHz) or faster 32-bit (x86) or 64-bit (x64) processor
1 gigabyte (GB) RAM (32-bit) or 2 GB RAM (64-bit)
16 GB available hard disk drive space (32-bit) or 20 GB (64-bit)
DirectX 9 graphics device with WDDM 1.0 or higher driver

Design points

Your solution should work on multiple computers.
Your solution should accept piped input for the computer names.
You should return a single object for each computer.
Your code should be reusable.
Your solution should have comment-based Help.
Your solution should use error handling.
Your solution should implement the –Debug switch.
Extra design points if your solution detects if the computer in question would be best upgraded to a 64-bit (x64) or to a32-bit (x86) Windows 7 operating system.


Mon script (une solution) :

[code:1]#
# 2011 Scripting Games : Advanced Event 5
# Script : Test-Win7Requirements.ps1
# Author : Matthew BETTON (France / Basse-Normandie / Manche (50))
# Date : 04/11/2011
# Synopsis : Use PowerShell to determine upgrade to Windows 7 eligibility
#

<#
.SYNOPSIS
This script determines upgrade to Windows 7 eligibility.
.DESCRIPTION
The 'Test-Win7Requirements.ps1' script returns informations about the eligibility of one or more computers to be upgraded to Windows 7.

You should pipe a list of computer names or use the '-ComputerName' parameter to collect informations for a computers list.

If the target computer is not a Windows XP based computer : the computer is not treated.

The script will only output informations (one object for each computer scanned) about Windows XP computers and there upgrade to Windows 7 eligibility.

If the computer can be upgraded to Windows 7 32 bits : The 'Upgrade32bits' boolean value of the output object must be $true. Otherwise the computer does not fulfill all the requirements.

If the computer can be upgraded to Windows 7 64 bits : The 'Upgrade64bits' boolean value of the output object must be $true. Otherwise the computer does not fulfill all the requirements.

Even if the Windows XP computer is not eligible at all, an object is returned, so you can next filter the output in order to keep only the computers you want.

This script uses cmdlet binding so it gets automatically the -Debug switch parameter, locally enabling Write-Debug. Otherwise, if you set the $DebugPreference variable to \"Continue\" before running this script, you will automatically get debugging informations into the console.

.PARAMETER ComputerName
Specifies the computer against which you want to run the management operation. The value can be a fully qualified domain name, a NetBIOS name, or an IP address.
Use the local computer name, use localhost, or use a dot (.) to specify the local computer.
When the remote computer is in a different domain from the user, you must use a fully qualified domain name.
This parameter can also be piped to the cmdlet.
This parameter does not rely on Windows PowerShell remoting, which uses WS-Management ).
You can use the ComputerName parameter of this script even if your computer is not configured to run WS-Management remote commands.

.PARAMETER Credential
Enter a PSCredential object, such as an object that is returned by the Get-Credential cmdlet.
The default is the current user.

.OUTPUTS
This script returns System.Management.Automation.PSCustomObject

.EXAMPLE
C:\PS>.\Test-Win7Requirements.ps1 -ComputerName \"Fabrikam01\"

This command determines upgrade to Windows 7 eligibility for computer \"Fabrikam01\".

.EXAMPLE
C:\PS>\"Fabrikam01\", \"Fabricam02\", \"MyComputer\" | .\Test-Win7Requirements.ps1

This command shows you that the computers names list can be piped to the script.

.EXAMPLE
C:\PS>$MyCredential = Get-Credential
C:\PS>.\Test-Win7Requirements.ps1 -ComputerName \"Fabrikam01\" -Credential $MyCredential

The first command asks for a credential.
The next command determines upgrade to Windows 7 eligibility for computer \"Fabrikam01\", by specifying a credential.

.EXAMPLE
C:\PS>$DebugPreference = \"Continue\"
C:\PS>$MyCredential = Get-Credential
C:\PS>.\Test-Win7Requirements.ps1 -ComputerName \"Fabrikam01\" -Credential $MyCredential

The first command set the $DebugPrefenrence variable to \"continue\", so debugging informations will be written to the Console
The second command asks for a credential.
The third command determines upgrade to Windows 7 eligibility for computer \"Fabrikam01\", by specifying a credential.

.EXAMPLE
C:\PS>$MyCredential = Get-Credential
C:\PS>$results = .\Test-Win7Requirements.ps1 -ComputerName \"Fabrikam01\", \"Fabrikam02\", \"Fabrikam03\" -Credential $MyCredential
C:\PS>$results | Where-Object{$_.Upgrade64bits -eq $true}

The first command asks for a credential.
The second command determines upgrade to Windows 7 eligibility for a list of computers, with specific credential.
The last command wil show you only the computers that could be upgraded to Windows 7 64 bits edition.

.EXAMPLE
C:\PS>$MyCredential = Get-Credential
C:\PS>$results = .\Test-Win7Requirements.ps1 -ComputerName \"Fabrikam01\", \"Fabrikam02\", \"Fabrikam03\" -Credential $MyCredential
C:\PS>$results | Format-Table Name, Upgrade32bits, Upgrade64bits

The first command asks for a credential.
The second command determines upgrade to Windows 7 eligibility for a list of computers, with specific credential.
The last command formats the output and shows you only the computers by name, Upgrade32bits (boolean) ans Upgrade64bits (boolean).

#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[System.String[]]$ComputerName,
[System.Management.Automation.PSCredential]$Credential = ([Management.Automation.PSCredential]::Empty)
)

Begin {
# Executes once before first item in pipeline is processed

# This functions returns an object containig computer's system informations
# For an error or an off topic computer : returns $null
Function SystemInfoWorker([String]$Computer,[System.Management.Automation.PSCredential]$Cred){
Write-Debug \">>>>>> Function 'SystemInfoWorker' called\"
Write-Debug \">>>>>> 'Computer' = $Computer\"
Write-Debug \">>>>>> 'Credential' = $($Cred.UserName)\"

try{
Write-Debug \"Verifying if target computer $Computer is a Windows XP based computer ...\"
$OperatingSystems = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer -Credential $Credential -ErrorAction Stop
}
catch{
Write-Debug \"An error has occured while checking the operating system on $Computer !\"
return $null
}

if($OperatingSystems.Caption -notlike \"*Windows XP*\"«»){
Write-Debug \"$Computer is not a Windows XP based computer : $($OperatingSystems.Caption)\"
return $null
}
else{
Write-Debug \"$Computer is a Windows XP based computer : $($OperatingSystems.Caption)\"
}

try{
Write-Debug \"Getting processor informations from $Computer (WMI / Win32_Processor) ...\"
$CptProcessor = Get-WmiObject -Class Win32_Processor -ComputerName $Computer -Credential $Cred -ErrorAction Stop | Where-Object{$_.DeviceID -eq \"CPU0\"}
}
catch{
Write-Debug \"An error has occured while checking the operating system on $Computer : '$($Error[0].Exception.Message)'\"
return $null
}
Write-Debug \"Determining the processor architecture for $Computer ...\"
switch($CptProcessor.Architecture){
0{$Architecture = \"x86\"}
9{$Architecture = \"x64\"}
default{$Architecture = \"?\"}
}

if($Architecture -eq \"?\"«»){
Write-Debug \"The processor architecture of $Computer is not 'x86' or 'x64'\"
return $null
}
else{
Write-Debug \"$Computer has a $Architecture based processor\"
}

try{
Write-Debug \"Getting system informations from $Computer (WMI / Win32_ComputerSystem) ...\"
$CptSystem = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer -Credential $Cred -ErrorAction Stop

Write-Debug \"Getting system volume informations from $Computer (WMI / Win32_LogicalDisk) ...\"
$CptLogicalDisk = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $Computer -Credential $Cred -ErrorAction Stop | Where-Object{$_.DeviceID -eq \"C:\"}

Write-Debug \"Getting directx version installed on $Computer, from its registry (WMI / StdRegProv) ...\"
$hklm = 2147483650
$key = \"Software\Microsoft\DirectX\"
$value = \"Version\"
$objreg = Get-WmiObject -list -namespace root\default -computerName $Computer -Credential $Cred -ErrorAction SilentlyContinue | Where-Object {$_.name -eq \"StdRegProv\"}
$DirectXversion = ($objreg.GetStringValue($hklm,$key,$value).svalue).Substring(0,4)
if(-not $DirectXversion){
$DirectXversion = \"\"
}
}
catch{
Write-Debug \"An error has occured while collecting informations from $Computer : '$($Error[0].Exception.Message)'\"
return $null
}

# If no error has occured, we can manage collected informations
Write-Debug \"Interpreting $computer system informations ...\"
$Frequency = [Math]::Round(($CptProcessor.MaxClockSpeed / 1000),1)
Write-Debug \"Processor Frequency for $Computer (GHz) : $Frequency\"
$Memory = [int]($CptSystem.TotalPhysicalMemory / 1GB«»)
Write-Debug \"Total Physical Memory for $computer (GB«») : $Memory\"
$Freespace = [Math]::Round(($CptLogicalDisk.Freespace / 1GB«»),1)
Write-Debug \"Freespace for system volume on $computer (GB«») : $Freespace\"

Write-Debug \"Creating a new PSObject with computer informations as properties ...\"

$ObjComputer = New-Object PSOBject -Property @{
\"Name\" = $computer
\"ProcessorArchitecture\" = $Architecture
\"ProcessorFrequency\" = \"$Frequency GHz\"
\"TotalPhysicalMemory\" = \"$Memory GB\"
\"Freespace\" = \"$Freespace GB\"
\"DirectXversion\" = $DirectXversion[3]
}

Write-Debug \"Determining if $Computer is upgradable to Windows 7 32 bits and/or 64 bits ...\"
$BoolIsUpgradable32 = $true
$BoolIsUpgradable64 = $true
Write-Debug \"Testing requirement for the upgrade : 1 gigahertz (GHz) or faster 32-bit (x86) or 64-bit (x64) processor ...\"
if([int]$Frequency -lt 1){
$BoolIsUpgradable32 = $false
$BoolIsUpgradable64 = $false
}
Write-Debug \"Testing requirement for the upgrade : 1 gigabyte (GB«») RAM (32-bit) or 2 GB RAM (64-bit) ...\"
if([int]$Memory -lt 1){
$BoolIsUpgradable32 = $false
$BoolIsUpgradable64 = $false
}
else{
if([int]$Memory -lt 2){
$BoolIsUpgradable64 = $false
}
}
Write-Debug \"Testing requirement for the upgrade : 16 GB available hard disk drive space (32-bit) or 20 GB (64-bit) ...\"
if([int]$Freespace -lt 16){
$BoolIsUpgradable32 = $false
$BoolIsUpgradable64 = $false
}
else{
if($Freespace -lt 20){
$BoolIsUpgradable64 = $false
}
}
Write-Debug \"Testing requirement for the upgrade : DirectX 9 graphics device with WDDM 1.0 or higher driver ...\"
if($DirectXversion -ne '4.09'){
$BoolIsUpgradable32 = $false
$BoolIsUpgradable64 = $false
}
Write-Debug \"Testing requirement for a 64-bit upgrade : DirectX 9 graphics device with WDDM 1.0 or higher driver ...\"
if($Architecture -eq \"x86\"«»){
$BoolIsUpgradable64 = $false
}

Write-Debug \"Adding new properties to the computer's object ...\"
$ObjComputer | Add-Member -MemberType NoteProperty -Name \"Upgrade32bits\" -Value $BoolIsUpgradable32
$ObjComputer | Add-Member -MemberType NoteProperty -Name \"Upgrade64bits\" -Value $BoolIsUpgradable64

Write-Debug \"Returning informations for $Computer ...\"
return $ObjComputer

}

$TabCpts = $null
$TabCpts = @()

}

Process {
# Executes once for each pipeline object
if ($PSBoundParameters) {
Write-Debug \"'-ComputerName' parameter has been used\"
foreach($cpt in $ComputerName) {
$TabCpts += (SystemInfoWorker -Computer $cpt -Cred $Credential)
}
}
else{
$TabCpts += (SystemInfoWorker -Computer $ComputerName -Cred $Credential)
}

}

End {
# Executes once after last pipeline object is processed
Write-Debug \"Write the object(s) to the Output\"
$TabCpts

}[/code:1]



Pour les personnes que cela intéresse, voici le lien vers une solution d'un expert . ;)

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 14 ans 9 mois #9711 par Laurent Dardenne
Salut,
le truc qui me gêne le plus ici est d'avoir une fonction comportant de nombreuse lignes de code redondantes, enfin sur leur usage/objectif.

La solution de l'expert éclate le code, mais je trouves les deux approches trop 'statiques'. Je sais que le contexte d'un tel concours n'est pas propice à une réflexion poussée, mais l'usage du dynamisme faciliterait l'évolution du code.

Comme dernièrement j'ai été confronté à ce sujet, je pensais et penchais vers une solution plus proche d'un DSL, à la PSSake :P

Sinon une autre conception par James Brundage basée sur des plugins :
blogs.msdn.com/powershell/archive/2008/1...t-commandplugin.aspx

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 14 ans 9 mois #9782 par Matthew
Salut,

En fait ce qui nous manque le plus c'est le temps !

Heureusement qu'il existe des forums tels que celui-ci pour partager ce genre d'informations :cheer:

Merci Laurent pour ces infos qui démontrent encore une fois qu'on n'a jamais fini de s'améliorer et d'apprendre ;)

J'aime beaucoup le commentaire suivant (entre autres) dans l'article de James Brundage :

I'm reminded of a quote from a Marvel Movie: \"With Great Power Comes Great Responsibility.\"
Get-CommandPlugin has great power. Please script responsibly.

:P

Encore merci

@ +

Matthew<br><br>Message édité par: Matthew, à: 12/06/11 10:02

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.086 secondes
Propulsé par Kunena