Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
di Sergei Antonov
Introduzione
Con PowerShell fornito, gli amministratori di IIS ottengono un nuovo strumento da usare. L'articolo seguente si concentra sulle attività di amministrazione per IIS 7.0 e versioni successive; Tuttavia, PowerShell può essere usato per i server IIS 6.0 esistenti.
Questo articolo è incentrato sull'amministrazione del server IIS remoto tramite PowerShell nel computer client. A partire da questo articolo, questo è possibile solo se si usa il provider WMI fornito dal team IIS con Vista e Windows Server® 2008. In questo caso, non è necessario disporre di alcun elemento correlato a IIS nel computer client. WMI fornisce la connessione alla configurazione effettiva disponibile nel server remoto.
Annotazioni
È anche possibile usare Microsoft.Web.Administration in PowerShell per eseguire funzioni di amministrazione. Tuttavia, questo articolo non si concentra su questa tecnica.
Il team di PowerShell ha creato un comando speciale da usare per l'accesso agli oggetti WMI: get-wmiobject. In genere restituisce un oggetto, creato all'interno di PowerShell che espone proprietà e metodi WMI, anziché l'oggetto di codice gestito consueto restituito per le classi regolari.
Questo oggetto sintetico espone i metadati definiti nello spazio dei nomi WMI, non i metadati di System.Management.ManagementBaseObject usati come base. In questo modo l'utente visualizza lo spazio dei nomi esposto dal provider WMI e che riflette il modello di amministrazione dell'entità configurata.
Sfortunatamente, questo comando non funziona con gli spazi dei nomi IIS. Per PowerShell versione 1.0, get-wmiobject supporta solo il livello di autenticazione predefinito per la connessione DCOM remota. Questo non è sufficiente per IIS 6.0 o per IIS 7.0 e versioni successive. Quando gli utenti configurano IIS, potrebbe essere necessario inviare password e altri dati sensibili tramite la connessione di rete per modificarlo e archiviarlo nella configurazione. A tale scopo, i provider IIS richiedono un livello di autenticazione "Privacy dei pacchetti". Non è possibile fornire questo requisito al cmdlet get-wmiobject.
Con questa limitazione sono disponibili due opzioni:
- Usare PowerShell come interfaccia di scripting generica per lo spazio dei nomi System.Management. Usare PowerShell anche per eseguire le operazioni seguenti: scrivere codice script che configura la connessione allo spazio dei nomi WMI remoto; e, per recuperare e salvare i dati di amministrazione usando gli oggetti System.Management, a cui si accede tramite PowerShell. È come la programmazione C#, solo nel linguaggio PowerShell. In genere funziona bene, ma per il caso WMI, PowerShell applica una scheda speciale che converte automaticamente tutti gli oggetti System.Management in oggetti PowerShell sintetici che espongono lo spazio dei nomi WMI come API primaria. Per questo motivo, è necessario superare le modifiche dell'adapter e scrivere codice aggiuntivo per accedere alla sottostruttura System.Management "nativa", che trasforma rapidamente questa programmazione in un esercizio inutilmente complicato.
Di conseguenza, verrà esaminata l'altra opzione:
- Scrivere cmdlet di PowerShell in C# e accedere alle funzionalità necessarie nel codice C#. In questo caso, si scelgono le API appropriate per le entità configurate. Per accedere al server viene usato WMI. Rispetto allo stesso codice in PowerShell, C# è molto più efficace, perché non è necessario analizzarlo e interpretarlo ogni volta.
PowerShell Cmdlet
Per iniziare a scrivere i cmdlet, è necessario un computer client installato con PowerShell. È anche necessario installare PowerShell SDK o semplicemente copiare dll di riferimento nella cartella di lavoro usando il trucco pubblicato da Jeffrey Snover nel blog del team di PowerShell. Assicurarsi di avere la connessione DCOM nel server. Il modo più semplice per confermare questa operazione consiste nell'avviare l'utilità wbemtest, disponibile in ogni piattaforma Windows, e provare la connessione.
- Avvia wbemtest.
- Fare clic su Connetti.
- Immettere i parametri di connessione:
- Sostituire "root\default" per \<computer>\root\webadministration, dove "<computer>" deve essere il nome del server.
- Immettere le credenziali dell'account con diritti di amministratore nel server.
- Selezionare "Privacy dei pacchetti" nel gruppo "livello di autenticazione".
- Fare clic su Connetti. WMI nel computer client si connette al servizio WMI nel computer server. Se non è accessibile, viene visualizzata una finestra di dialogo di messaggio di errore.
- Eseguire un'azione semplice che coinvolge il provider WMI nella casella del server per verificare che funzioni. Eseguire un'enumerazione dei siti:
- Fare clic sul pulsante "Enum Instances" (Istanze Enum) e immettere "site" come nome della classe.
- Quando funziona, nella finestra di dialogo risultante viene visualizzato un elenco di tutti i siti disponibili nel server.
Il cmdlet di PowerShell è semplicemente un assembly di codice gestito implementato seguendo le regole formali, documentate in PowerShell SDK. Trovali online.
Prima di scrivere codice, è utile avere un piano.
Implementare innanzitutto un cmdlet che enumera tutti i siti IIS nel server remoto. Questo cmdlet restituisce una matrice di oggetti sito che rappresentano l'elemento di configurazione IIS con proprietà definite per il sito. Verranno aggiunte alcune proprietà aggiuntive utili per l'oggetto.
Si vuole che il cmdlet sia simile al seguente:
get-iissite –computer somecomputer –name somesite –credential $(get-credential)
Se non si passano le credenziali al cmdlet a livello di codice, PowerShell genera una finestra di dialogo che richiede il nome utente e la password per il server remoto.
Per ottenere l'oggetto sito dal computer remoto, è necessario specificare i parametri seguenti dal cmdlet :
public string Computer;
public string Name;
public PSCredential Credential;
Tutti questi parametri sono proprietà pubbliche nella classe cmdlet, decorata dall'attributo Parameter.
Implementare il primo cmdlet. Poiché get-iissite non è l'ultimo comando, è meglio fare due cose: separare il codice responsabile della connessione al server e inserirlo nella classe padre RemotingCommand; e, ereditare il cmdlet da quella classe.
using System;
using System.Net;
using System.Management;
using System.Management.Automation;
using System.ComponentModel;
using System.Security;
namespace Microsoft.Samples.PowerShell.IISCommands
{
public class RemotingCommand : PSCmdlet
{
private string computer = Environment.MachineName;
[Parameter(
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true)]
[ValidateNotNullOrEmpty]
public string[] Computer
{
get { return computer; }
set { computer = value; }
}
private PSCredential credential = null;
[Parameter(
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true)]
[CredentialAttribute]
public PSCredential Credential
{
get { return credential; }
set { credential = value; }
}
protected ManagementScope GetScope(string computerName)
{
ConnectionOptions connection = new ConnectionOptions();
connection.Username = Credential.UserName;
connection.Password = Credential.GetNetworkCredential().Password;
connection.Impersonation = ImpersonationLevel.Impersonate;
connection.Authentication = AuthenticationLevel.PacketPrivacy;
ManagementScope scope = new ManagementScope(
"\\\\" + computerName + "\\root\\webadministration", connection);
return scope;
}
protected override void EndProcessing()
{
if (null == credential)
{
// Check variable first
object varCred = GetVariableValue("IISCredential");
if (varCred != null && varCred.GetType() == typeof(PSObject))
{
credential = ((PSObject)varCred).BaseObject as PSCredential;
}
if (null == credential)
{
// use credential of current user or process
SecureString ss = new SecureString();
foreach (char c in CredentialCache.DefaultNetworkCredentials.Password.ToCharArray())
{
ss.AppendChar(c);
}
credential = new PSCredential(
CredentialCache.DefaultNetworkCredentials.UserName, ss);
}
}
}
protected ManagementClass CreateClassObject(
string computerName,
string classPath )
{
return new ManagementClass(
GetScope(computerName),
new ManagementPath(classPath),
new ObjectGetOptions()
);
}
}
La classe RemotingCommand include parametri e metodi necessari per la connessione.
- GetScope() restituisce l'oggetto System.Management con tutte le informazioni necessarie per la connessione allo spazio dei nomi remoto. Esaminare la connessione. Proprietà di autenticazione. Viene inizializzato in AuthenticationLevel.PacketPrivacy. Si tratta di un requisito obbligatorio. In caso contrario, WMI rifiuterà la connessione.
- Il metodo CreateClassObject() è un metodo di utilità che usa l'ambito di connessione per creare una classe specificata in base allo spazio dei nomi WMI remoto.
- Il metodo EndProcessing() è il metodo standard che ogni classe di cmdlet deve implementare. Viene invocato da PowerShell quando il nostro cmdlet viene elaborato. L'implementazione di EndProcessing() tenta di riempire la proprietà delle credenziali, se è vuota. Al primo tentativo, ottiene le credenziali dalla variabile esterna IISCredential (solo per praticità).
Quando si usa una sessione di PowerShell, l'utente potrebbe voler inserire il nome utente e la password in questa variabile e usarlo più volte in più comandi. Se questa variabile non è definita o contiene un tipo di oggetto non adatto, il codice ottiene le credenziali dell'utente o del processo corrente. Funziona quando l'utente esegue questo comando localmente nel server, usando l'account amministratore. In questo caso, non è necessario immettere alcuna credenziale. Per ogni ciclo in questo codice, esiste un trucco noto per convertire la password da stringa in SecureString.
A questo momento si implementa get-iissite.
[Cmdlet(VerbsCommon.Get, "IISSite")]
public class GetSiteCommand : RemotingCommand
{
private string name = null;
[Parameter(
Position = 0,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true)]
public string Name
{
get { return name; }
set { name = value; }
}
protected override void EndProcessing()
{
base.EndProcessing();
ManagementObjectCollection sites = CreateClassObject(computerName, "Site").GetInstances();
foreach (ManagementObject site in sites)
{
string siteName = site.GetPropertyValue("Name") as string;
if (Name != null)
{
if (siteName.Equals(Name, StringComparison.InvariantCultureIgnoreCase))
{
WriteObject(siteName);
break;
}
}
else
{
WriteObject(siteName);
}
}
}
} //GetSiteCommand
//
// [RunInstaller(true)]
// public class IISDemoCmdSnapIn : PSSnapIn {…}
//
} // Microsoft.Samples.PowerShell.IISCommands
Nel primo passaggio, il comando restituisce solo i nomi dei siti web. Se l'utente vuole un sito specifico, deve fornire il nome del sito al comando; in caso contrario, verranno restituiti tutti i siti in quel particolare computer.
Per completare il comando, è necessario aggiungere l'implementazione della classe , ereditata da PSSnapin. Questa classe viene usata per registrare i comandi. Non ha nulla di specifico per IIS; vedere il codice completo nel file di origine IISDemoCmd.cs.
Compilare ora il cmdlet e vedere come funziona. È possibile farlo da Visual Studio, ma è abbastanza semplice da compilarlo dalla riga di comando. Si supponga di inserire DLL di riferimento di PowerShell in una cartella c:\sdk. La riga di comando seguente compila il cmdlet in IISDemoCmd.dll e lo inserisce nella stessa cartella, in cui si trova il file di origine
%windir%\Microsoft.NET\Framework\v2.0.50727\csc /t:library /r:c:\sdk\system.management.automation.dll IISDemoCmd.cs
È ora necessario registrare il comando e aggiungerlo in PowerShell. Questa procedura è descritta in Guida di riferimento per la programmazione di PowerShell. Avviare PowerShell ed eseguire i comandi seguenti dalla stessa cartella in cui è stata compilata la DLL del cmdlet.
>set-alias installutil $env:windir\Microsoft.NET\Framework\v2.0.50727\installutil
>installutil iisdemocmd.dll
>add-pssnapin IISDemoCmdSnapIn
Questo aggiunge il cmdlet all'istanza in esecuzione di PowerShell. Salvare le righe di comando in un file di script. Verranno usati di nuovo man mano che si continua a lavorare sui cmdlet. Questo script viene trovato nel file demo_install.ps1.
Verificare se il comando è disponibile:
>get-command Get-IISSite
CommandType Name Definition
----------- ---- ----------
Cmdlet Get-IISSite Get-IISSite [[-Name] <String...
Ora provalo. Si supponga di connettersi al computer test_server usando l'account amministratore locale.
>Get-IISSite -computer test_server -credential $(get-credential administrator)
Default Web Site
Foo
Bar
Questa riga di comando riceve l'oggetto credenziali dal cmdlet get-credential, che interagirà con l'utente per ottenere la password. È anche possibile produrre le credenziali a livello di codice, ma è necessario digitare la password nello script, che non è affatto sicura.
>$global:iiscredential = new-object System.Management.Automation.PsCredential "Administrator",$(convertto-securestring "password" -asplaintext -force)
Questo comando archivia le credenziali nella variabile globale $iiscredential e il cmdlet lo userà automaticamente. Nelle sitautions reali, tuttavia, è preferibile archiviare le credenziali in una variabile usando il comando get-credential: $global:iiscredential = get-credential Administrator.
Ripeti ora quel comando.
>Get-IISSite -computer test_server
Default Web Site
Foo
Bar
Tutta l'infrastruttura è al suo posto. Ora, torna al comando e aggiungi il resto dei dati del sito.
Aggiunta di dati di configurazione al sito
È necessario convertire l'oggetto da ManagementBaseObject a PSObject e restituirlo in PowerShell. PSObject è un contenitore a mano libera che può essere riempito con diversi tipi di dati. Useremo il tipo PSNoteProperty. Mantenere pulito il codice del cmdlet e aggiungere una nuova classe responsabile della conversione.
class ObjectConverter
{
public static PSObject ToPSObject(
ManagementBaseObject source
)
{
PSObject obj = new PSObject();
foreach (PropertyData pd in source.Properties)
{
if (pd.Value.GetType() == typeof(System.Management.ManagementBaseObject))
{
obj.Properties.Add(new PSNoteProperty(
pd.Name, ObjectConverter.ToPSObject(pd.Value as ManagementBaseObject)
));
}
else if (pd.Value.GetType() == typeof(ManagementBaseObject[]))
{
ManagementBaseObject[] ar = pd.Value as ManagementBaseObject[];
PSObject[] psar = new PSObject[ar.Length];
for (int i = 0; i < ar.Length; ++i)
{
psar[i] = ObjectConverter.ToPSObject(ar[i]);
}
obj.Properties.Add(new PSNoteProperty(pd.Name, psar));
}
else
{
obj.Properties.Add(new PSNoteProperty(pd.Name, pd.Value));
}
}
return obj;
}
}
Questo codice fa ricorso a proprietà complesse e aggiunge proprietà semplici come PSNoteProperty al PSObject risultante. Si aggiunge anche un metodo dedicato che gestisce la conversione in un cmdlet out. Questo metodo converte tutti i dati WMI e aggiunge altre due proprietà: il nome del computer e le credenziali utilizzate per ottenere la connessione al computer. Ciò consente di distinguere ogni sito da altri oggetti nella sessione di PowerShell.
private PSObject ConstructPSSite(
string computerName,
ManagementObject site)
{
PSObject pssite = ObjectConverter.ToPSObject(site);
pssite.Properties.Add(new PSNoteProperty("Computer", computerName));
pssite.Properties.Add(new PSNoteProperty("Credential", Credential));
return pssite;
}
Sostituire il nome del sito restituito a PowerShell con un intero oggetto. Il metodo EndProcessing() nel cmdlet è ora simile al seguente:
protected override void EndProcessing()
{
base.EndProcessing();
ManagementObjectCollection sites = CreateClassObject(Computer, "Site").GetInstances();
foreach (ManagementObject site in sites)
{
string siteName = site.GetPropertyValue("Name") as string;
if (Name != null)
{
if (siteName.Equals(Name, StringComparison.InvariantCultureIgnoreCase))
{
WriteObject(ConstructPSSite(Computer, site));
break;
}
}
else
{
WriteObject(ConstructPSSite(Computer, site));
}
}
}
Quando si ripete la compilazione e la registrazione e si esegue di nuovo il comando, vengono visualizzati altri dati sul sito:
> get-iissite -computer test-server "default web site"
ApplicationDefaults : @{ApplicationPool=; EnabledProtocols=http; Path=}
Bindings : {@{BindingInformation=*:80:; Protocol=http}}
Id : 1
Limits : @{ConnectionTimeout=00000000000200.000000:000; Max
Bandwidth=4294967295; MaxConnections=4294967295}
LogFile : @{CustomLogPluginClsid=; Directory=%SystemDrive%\i
netpub\logs\LogFiles; Enabled=True; LocalTimeRollo
ver=False; LogExtFileFlags=2199503; LogFormat=2; P
eriod=1; TruncateSize=20971520}
Name : Default Web Site
ServerAutoStart : True
TraceFailedRequestsLogging : @{Directory=%SystemDrive%\inetpub\logs\FailedReqLo
gFiles; Enabled=False; MaxLogFiles=50}
VirtualDirectoryDefaults : @{AllowSubDirConfig=True; LogonMethod=3; Password=
; Path=; PhysicalPath=; UserName=}
Computer : test-server
Credential : System.Management.Automation.PSCredential
Se si confronta questo schema con lo schema WMI per il sito, si noterà che tutti i dati sono ora disponibili; inoltre, sono disponibili proprietà aggiuntive aggiunte nel cmdlet . Tutte le proprietà sono accessibili da PowerShell tramite la notazione "punto".
> $sites = get-iissite -computer test-server
>$sites[0]
ApplicationDefaults : @{ApplicationPool=; EnabledProtocols=http; Path=}
Bindings : {@{BindingInformation=*:80:; Protocol=http}}
Id : 1
Limits : @{ConnectionTimeout=00000000000200.000000:000; Max
Bandwidth=4294967295; MaxConnections=4294967295}
LogFile : @{CustomLogPluginClsid=; Directory=%SystemDrive%\i
netpub\logs\LogFiles; Enabled=True; LocalTimeRollo
ver=False; LogExtFileFlags=2199503; LogFormat=2; P
eriod=1; TruncateSize=20971520}
Name : Default Web Site
ServerAutoStart : True
TraceFailedRequestsLogging : @{Directory=%SystemDrive%\inetpub\logs\FailedReqLo
gFiles; Enabled=False; MaxLogFiles=50}
VirtualDirectoryDefaults : @{AllowSubDirConfig=True; LogonMethod=3; Password=
; Path=; PhysicalPath=; UserName=}
Computer : test-server
Credential : System.Management.Automation.PSCredential
>$sites[0].Limits
ConnectionTimeout MaxBandwidth MaxConnections
----------------- ------------ --------------
00000000000200.000000:000 4294967295 4294967295
> $sites[0].Limits.MaxBandwidth
4294967295
Abbiamo fatto buoni progressi, ma non abbastanza buoni. Il sito WMI include anche metodi, quindi provare ad aggiungerli anche. Questa operazione è semplice da eseguire in PowerShell: assegnare un nome al metodo e indicare a PowerShell dove si trova il codice. Verranno aggiunti metodi del tipo PSCodeMethod. Per mantenere il codice per i metodi, aggiungiamo la classe SiteMethods.
public class SiteMethods
{
static public void Start(PSObject site)
{
InvokeMethod(site, "Start");
}
static public void Stop(PSObject site)
{
InvokeMethod(site, "Stop");
}
static public string GetStatus(PSObject site)
{
uint status = (uint)InvokeMethod(site, "GetState");
string statusName =
status == 0 ? "Starting" :
status == 1 ? "Started" :
status == 2 ? "Stopping" :
status == 3 ? "Stopped" : "Unknown";
return statusName;
}
static private object InvokeMethod(PSObject site, string methodName)
{
string computerName = site.Properties["Computer"].Value as string;
string siteName = site.Properties["Name"].Value as string;
PSCredential credential = site.Properties["Credential"].Value as PSCredential;
ConnectionOptions connection = new ConnectionOptions();
connection.Username = credential.UserName;
connection.Password = credential.GetNetworkCredential().Password;
connection.Impersonation = ImpersonationLevel.Impersonate;
connection.Authentication = AuthenticationLevel.PacketPrivacy;
ManagementScope scope = new ManagementScope(
"\\\\" + computerName + "\\root\\webadministration", connection);
string sitePath = "Site.Name=\"" + siteName + "\"";
ManagementObject wmiSite = new ManagementObject(
scope, new ManagementPath(sitePath), new ObjectGetOptions());
return wmiSite.InvokeMethod(methodName, new object[] { });
}
}
Come si può notare, questo codice crea un oggetto WMI per il sito e chiama i metodi WMI su questo oggetto. Questo codice usa due proprietà aggiuntive aggiunte al sito. Con questa classe, è possibile estendere il metodo ConstructPSSite. È inoltre necessario aggiungere un riferimento allo spazio dei nomi System.Reflection.
private PSObject ConstructPSSite(
string computerName,
ManagementObject site)
{
PSObject pssite = ObjectConverter.ConvertSiteToPSObject(site);
pssite.Properties.Add(new PSNoteProperty("Computer", computerName));
pssite.Properties.Add(new PSNoteProperty("Credential", Credential));
Type siteMethodsType = typeof(SiteMethods);
foreach (MethodInfo mi in siteMethodsType.GetMethods())
{
if (mi.Name.Equals("Start", StringComparison.InvariantCultureIgnoreCase))
{
pssite.Methods.Add(new PSCodeMethod("Start", mi));
}
if (mi.Name.Equals("Stop", StringComparison.InvariantCultureIgnoreCase))
{
pssite.Methods.Add(new PSCodeMethod("Stop", mi));
}
if (mi.Name.Equals("GetStatus", StringComparison.InvariantCultureIgnoreCase))
{
pssite.Properties.Add(new PSCodeProperty("Status", mi));
}
}
return pssite;
}
Oltre ai metodi aggiunti, è presente una proprietà dinamica, ovvero "Status". Si comporta allo stesso modo delle proprietà nelle classi C#; si tratta di una funzione chiamata quando PowerShell ha bisogno del relativo valore. Il codice è molto semplice, perché si fa riferimento a metodi dello stesso assembly del cmdlet. Nulla impedisce il caricamento di qualsiasi altro assembly e di ottenere le informazioni sui metodi delle relative classi. Se questi metodi hanno la firma corretta, PowerShell lo usa allo stesso modo.
L'oggetto è ora simile al seguente:
>$s = get-iissite "Default Web Site" –computer test-server
> $s | get-member
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Start CodeMethod static System.Void Start(PSObject site)
Stop CodeMethod static System.Void Stop(PSObject site)
Status CodeProperty System.String Status{get=GetStatus;}
Equals Method System.Boolean Equals(Object obj)
GetHashCode Method System.Int32 GetHashCode()
GetType Method System.Type GetType()
ToString Method System.String ToString()
ApplicationDefaults NoteProperty System.Management.Automation.PSObjec...
Bindings NoteProperty System.Management.Automation.PSObjec...
Computer NoteProperty System.String Computer=iissb-101
Credential NoteProperty System.Management.Automation.PSCrede...
Id NoteProperty System.UInt32 Id=1
Limits NoteProperty System.Management.Automation.PSObjec...
LogFile NoteProperty System.Management.Automation.PSObjec...
Name NoteProperty System.String Name=Default Web Site
ServerAutoStart NoteProperty System.Boolean ServerAutoStart=True
TraceFailedRequestsLogging NoteProperty System.Management.Automation.PSObjec...
VirtualDirectoryDefaults NoteProperty System.Management.Automation.PSObjec...
>$s.Status
Started
> $s.Stop()
> $s.Status
Stopped
> $s.Start()
> $s.Status
Started
Con la possibilità di aggiungere metodi e proprietà dinamiche agli oggetti, è possibile sintetizzare ciò di cui abbiamo bisogno per qualsiasi situazione. Oltre ai metodi e alle proprietà aggiunti nel cmdlet, è possibile aggiungere altro nello script, senza dover usare il codice C#.
È anche possibile caricare la definizione dell'oggetto da XML. Un buon candidato per proprietà aggiuntive è costituito dai dati esposti da IIS tramite contatori delle prestazioni correlati al sito, ad esempio il conteggio totale delle richieste elaborate. Questi dati sono facilmente accessibili direttamente dal codice gestito. Non è necessario usare WMI.
Come chiamare un cmdlet da un altro cmdlet
Ottenere oggetti del sito è importante, ma abbiamo bisogno di più, ad esempio scrivere un comando per aggiungere un nuovo sito. Per creare siti, è possibile usare il metodo astratto Create() definito nella classe Site nello spazio dei nomi WMI WebAdministration. Il cmdlet è simile al seguente:
>add-iissite –name <siteName> -computer <serverName> -credential <credential> -bindings <array-of-bindings> –homepath <path> -autostart
Sono disponibili gli stessi parametri definiti nel metodo Create. Inoltre, il comando deve supportare le opzioni –whatif e –passthru. Il primo mostra il risultato dell'esecuzione del comando, ma non apporta alcuna modifica; il secondo indica al comando di restituire il risultato nella pipeline. Questi due interruttori sono altamente consigliati per l'uso nei comandi "distruttivi". Per supportare il cmdlet –whatif, la classe deve essere decorata dall'attributo SupportsShouldProcess = true.
Di seguito è riportato il codice (trovare l'intero codice nel iisdemocmd.cs).
[Cmdlet(VerbsCommon.Add, "IISSite", SupportsShouldProcess = true)]
public class AddSiteCommand : RemotingCommand
{
//…
private SwitchParameter passThru = new SwitchParameter(false);
[Parameter]
public SwitchParameter PassThru
{
get { return passThru; }
set { passThru = value; }
}
protected override void EndProcessing()
{
base.EndProcessing();
if (ShouldProcess(string.Format("{0} bound to {1} on {2}", name, bindings.ToString(), rootFolder)))
{
object[] args = new object[4];
args[0] = Name;
ManagementBaseObject[] mbarr = new ManagementBaseObject[bindings.Length];
for (int b = 0; b < bindings.Length; ++b)
{
mbarr[b] = ObjectConverter.ToManagementObject(
GetScope(Computer), "BindingElement", bindings[b]);
}
args[1] = mbarr;
args[2] = rootFolder;
args[3] = autoStart;
ManagementClass siteClass = CreateClassObject(Computer, "Site");
try
{
siteClass.InvokeMethod("Create", args);
}
catch (COMException comEx)
{
WriteError(new ErrorRecord(comEx, comEx.Message, ErrorCategory.InvalidArgument, Name));
}
if (PassThru.IsPresent)
{
string getSiteScript = "get-iissite"
+ " -name " + Name
+ " -computer " + Computer
+ " -credential $args[0]";
this.InvokeCommand.InvokeScript(
getSiteScript, false, PipelineResultTypes.Output, null, Credential);
}
}
}
}
Questo codice usa un nuovo metodo nella classe ObjectConverter per produrre una matrice di associazioni. Il metodo ToManagementObject() converte i parametri di input che possono essere PSObject o Hashtable nell'istanza della classe ManagementBaseObject. Poiché la chiamata a Create potrebbe non riuscire con parametri perfettamente corretti se il sito con tali parametri è già disponibile, questo metodo viene chiamato in try/cach.
Infine, se il cmdlet verifica se l'utente specificato –passthru è presente, chiama PowerShell per eseguire un frammento di script che restituisce questo nuovo sito. In questo script viene chiamato il comando "get-iissite" e vengono riutilizzati i parametri passati al comando corrente. InvokeScript inserisce il risultato nella pipeline come richiesto, quindi non è necessario eseguire altre operazioni. Questo è un esempio di come eseguire il "callback" a PowerShell, passando le righe di comando formattate in modo statico o dinamico. Naturalmente, è possibile scriverlo come codice C#, ma richiede l'operazione di taglio e incollamento di grandi parti di GetSiteCommand o la riorganizzazione e il refactoring dello spazio dei nomi.
All'inizio del metodo EndProcessing() viene visualizzata la chiamata a ShouldProcess(). Questo è il modo in cui è supportata l'opzione –whatif. Quando l'utente passa questa opzione, questo metodo stampa il testo passato come parametro e il valore restituito è false. Tutte le azioni che possono modificare l'ambiente devono essere eseguite quando questa chiamata restituisce true. PowerShell include altre opzioni che possono interagire con l'utente e chiedere conferma prima di eseguire qualsiasi azione. ShouldProcess() restituisce in seguito a questa conferma.
Testare il nuovo comando con quanto segue:
> add-iissite Foo @{Protocol="http"; BindingInformation="*:808"} e:\inetpub\demo -computer test-server -whatif
What if: Performing operation "Add-IISSite" on Target "Foo bound to System.Management.Automation.PSObject[] on e:\inetpub\demo".
Questo è il funzionamento di –whatif. Viene visualizzato un messaggio piuttosto criptico su cosa accade quando questo comando viene eseguito. Per renderlo più chiaro, è necessario formattarlo correttamente. Le Associazioni di Parametri vengono immesse nella riga di comando come tabella hash e vengono passate al cmdlet come tabella hash incapsulata in PSObject. Per produrre testo significativo da esso, è necessario aggiungere più codice intelligente: il valore predefinito ToString() restituisce semplicemente il nome della classe.
Inserire questo blocco di testo al posto della riga ShouldProcess():
StringBuilder bindingText = new StringBuilder("(");
foreach (PSObject b in bindings)
{
Hashtable ht = b.BaseObject as Hashtable;
foreach (object key in ht.Keys)
{
string bstr = String.Format("{0}={1}",
key.ToString(), ht[key].ToString());
bindingText.Append(bstr + ",");
}
bindingText.Remove(bindingText.Length - 1, 1);
bindingText.Append(";");
}
bindingText.Remove(bindingText.Length - 1, 1);
bindingText.Append(")");
if (ShouldProcess(string.Format("{0} bound to {1} on {2}", name, bindingText.ToString(), rootFolder)))
Dopo aver compilato ed eseguito il cmdlet, viene visualizzato l'output seguente:
> add-iissite Foo @{Protocol="http"; BindingInformation="*:888"} e:\inetpub\demo -computer test-server -whatif
What if: Performing operation "Add-IISSite" on Target "Foo bound to (BindingInformation=*:888,Protocol=http) on e:\inetpub\demo".
Questo è molto più comprensibile. Da questo nuovo codice, è anche chiaro perché è necessario elaborare Hashtable nel metodo ToManagementObject(): si tratta di un tipo comune in PowerShell per passare parametri strutturati.
Eseguire ora il comando .
> add-iissite Foo @{Protocol="http"; BindingInformation="*:888"} e:\inetpub\demo -computer test-server -passthru | format-table Name,Status
Name Status
---- ------
Foo Stopped
> get-iissite -computer sergeia-a | format-table name,status
Name Status
---- ------
Default Web Site Started
Foo Stopped
Il primo comando ha creato il sito nel server remoto e quindi lo ha recuperato e passato alla pipeline. Per assicurarsi che sia stato fatto correttamente, abbiamo ottenuto un elenco dei siti, e in effetti, il nuovo sito è disponibile. Per impostazione predefinita, il server tenterà di avviare il sito, a meno che non si aggiunga il parametro –AutoStart false. Se si verifica un problema nei parametri, ad esempio, il server non riesce a trovare la home folder, il sito rimarrà arrestato.
Estensione dei cmdlet per l'uso con una server farm
Per il momento sono disponibili due comandi: get-iissite e add-iissite. Mancano i cmdlet per salvare il sito modificato e per eliminare il sito. Il comando di eliminazione deve essere remove-iissite per mantenerlo compatibile con gli standard di denominazione di PowerShell. Il comando Save avrà un nome set-iissite. Per remove-iissite, si modifica il codice get-iissite e si chiama il metodo Delete() in ManagementObject.
[Cmdlet(VerbsCommon.Remove, "IISSite", SupportsShouldProcess = true)]
public class RemoveSiteCommand : RemotingCommand
{
private string name = null;
[Parameter(
Position = 0,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true)]
[ValidateNotNullOrEmpty]
public string Name
{
get { return name; }
set { name = value; }
}
protected override void EndProcessing()
{
base.EndProcessing();
if (ShouldProcess(string.Format("{0} on server {1}", name, Computer)))
{
ManagementObject site = CreateClassInstance(Computer, "Site.Name=\"" + Name + "\"");
site.Delete();
}
}
} //RemoveSiteCommand
È stato anche aggiunto un metodo semplice CreateClassInstance() al cmdlet padre. Questo metodo produce l'istanza dell'oggetto associata al percorso dell'oggetto. Un'altra modifica è che il parametro Name non può essere vuoto. In caso contrario, l'utente può eliminare tutti i siti per errore. Infine, è stata aggiunta la chiamata ShouldProcess() per abilitare le opzioni –whatif e –confirm.
> Remove-IISSite foo -computer test-server -confirm
Confirm
Are you sure you want to perform this action?
Performing operation "Remove-IISSite" on Target "foo on server sergeia-a".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help
(default is "Y"): <CR>
> get-iissite -computer test-server | ft name,status
Name Status
---- ------
Default Web Site Started
È possibile implementare l'ultimo comando set-iissite come esercizio, modificando il cmdlet add-iissite e chiamando Put() in ManagementObject.
Ridimensionare ora i comandi e adattarli per lavorare con più server. Questo è facile:
Modificare la proprietà Computer nel comando padre per rappresentare una matrice di stringhe:
private string[] computer = { Environment.MachineName };
[Parameter(
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true)]
[ValidateNotNullOrEmpty]
public string[] Computer
{
get { return computer; }
set { computer = value; }
}
Aggiungere quindi un ciclo aggiuntivo su questa matrice in ogni cmdlet per eseguire la stessa azione in ogni computer. Di seguito è riportato un esempio di get-iissite:
foreach (string computerName in Computer)
{
ManagementObjectCollection sites = CreateClassObject(computerName, "Site").GetInstances();
foreach (ManagementObject site in sites)
{
if (Name != null)
{
string siteName = site.GetPropertyValue("Name") as string;
if (siteName.Equals(Name, StringComparison.InvariantCultureIgnoreCase))
{
WriteObject(ConstructPSSite(computerName, site));
break;
}
}
else
{
WriteObject(ConstructPSSite(computerName, site));
}
}
}
È ora possibile modificare i siti nell'intera server farm.
> get-iissite -computer test-server,iissb-101,iissb-102 | ft Computer,Name,Status
Computer Name Status
-------- ---- ------
test-server Default Web Site Started
iissb-101 Default Web Site Started
iissb-101 Demo Started
iissb-102 Default Web Site Started
Salvare i nomi dei server dalla farm nel file di testo e usarli come parametro:
>$("test-server","iissb-101","iissb-102" >farm.txt
>cat farm.txt
test-server
tissb-101
tissb-102
>get-iissite –computer $(cat farm.txt) | ft Computer,Name,Status
Computer Name Status
-------- ---- ------
test-server Default Web Site Started
iissb-101 Default Web Site Started
iissb-101 Demo Started
iissb-102 Default Web Site Started
>get-iissite –computer $(cat farm.txt) | where {$_.Computer –like "iissb*"} | ft Computer,Name,Status
Computer Name Status
-------- ---- ------
iissb-101 Default Web Site Started
iissb-101 Demo Started
iissb-102 Default Web Site Started
È possibile eseguire operazioni più avanzate usando un linguaggio di PowerShell più avanzato. Il codice seguente enumera i siti nei server con nomi. A partire da "iissb", memorizzare l'elenco nella variabile e quindi fermare tutti i siti avviati.
> $sitelist = get-iissite -computer $(cat farm.txt) | where {$_.Computer -like "iissb*"}
> foreach ($site in $sitelist) {
>> if ($site.Status -eq "Started") {$site.Stop()}
>> }
>>
> get-iissite -computer $(cat farm.txt) | ft Computer,Name,Status
Computer Name Status
-------- ---- ------
test-server Default Web Site Started
iissb-101 Default Web Site Stopped
iissb-101 Demo Stopped
iissb-102 Default Web Site Stopped
La variabile $sitelist mantiene l'elenco dei siti, ma grazie alla natura dinamica del sito di proprietà. Stato, viene visualizzato lo stato effettivo, non archiviato, di ogni oggetto.
> $sitelist | ft computer,name,status
Computer Name Status
-------- ---- ------
iissb-101 Default Web Site Stopped
iissb-101 Demo Stopped
iissb-102 Default Web Site Stopped
È possibile eseguire la stessa operazione senza usare alcuna variabile. Riavviare qualsiasi sito arrestato in qualunque server della server farm.
> get-iissite -computer (cat farm.txt) | foreach { if ($_.Status -eq "Stopped") { $_.Start() }}
> get-iissite -computer $(cat farm.txt) | ft Computer,Name,Status
Computer Name Status
-------- ---- ------
test-server Default Web Site Started
iissb-101 Default Web Site Started
iissb-101 Demo Started
iissb-102 Default Web Site Started
Nel file di origine adiacente iisdemocmd.cs sono disponibili altri comandi per la modifica delle directory virtuali e alcune proprietà nelle sezioni di configurazione.
Conclusione
Come si può notare, la presenza di soli tre comandi consente di coprire la maggior parte delle esigenze nell'amministrazione dei siti IIS. In combinazione con la flessibilità e la ricchezza del linguaggio della shell, ogni comando aggiunge una grande quantità di funzionalità. Allo stesso tempo, la scrittura di un nuovo comando non è molto più complessa rispetto all'implementazione di script simili in VBScript o Jscript.
Il team iis prevede di aggiungere il supporto completo di PowerShell in IIS 7.0 e versioni successive. Ciò include l'implementazione di un provider di navigazione, un provider di proprietà e tutte le altre funzionalità necessarie per lavorare con tutti gli aspetti dell'amministrazione. Segui i progressi di questi miglioramenti imminenti e cerca l'annuncio su https://www.iis.net/ e sul sito PowerShell.