Pierwsze koty za płoty. ;)

•19 września, 2011 • Dodaj komentarz

Dziś tylko króciutki wpis…

W zeszłym tygodniu miałem wyjątkową przyjemność zaprezentować przed specjalistami od Exchange’a część mojej wiedzy na temat PowerShella. Pierwszy raz miałem okazję mówić o PowerShellu do “żywych ludzi” i to polsku. Do tej pory zdarzyło mi się tylko kilka razy szkolić via WebEx kolegów z innych biur, siłą rzeczy – po angielsku. Najtrudniej jest oceniać samego siebie, więc nie napiszę jak mi poszło – mam podejrzenie jednak, że nieco mnie poniosło. Mam tylko nadzieję, że nikogo do PowerShella nie zniechęciłem. 😉

Załączam samą prezentację wraz z plikami .ps1, których użyłem w jej trakcie.

Jeśli któryś z uczestników zajrzy tu kiedyś, to prosiłbym o informację: jak to wyglądało z Waszego punktu widzenia. Każda uwaga (zwłaszcza krytyczna) na pewno się przyda. 🙂

W zasadzie treść tej prezentacji będzie punktem wyjścia do zapowiadanego wcześniej cyklu postów, które planuję w tym miesiącu (i ew. na początku przyszłego) opublikować na moim blogu. Pierwsza część być może już jutro. Trzymajcie kciuki. 😉

Osiołkowi w żłoby dano…

•8 września, 2011 • 1 komentarz

Witam po wakacjach!

Moje w tym roku były wybitnie za krótkie, zbyt mało miałem czasu by się zatrzymać, wypocząć i przemyśleć to i owo. Ambitny plan by odwiedzić większość rodziny się powiódł, ale koszt tej wyprawy – zarówno dla maluchów jak i dla miłej żonki i mnie był chyba jednak za wysoki…

Wróciłem do pracy i w zasadzie o dwóch tygodni próbuję nadgonić zaległości. Nie pomaga w tym fakt, że jednocześnie zmuszony jestem wdrażać naszego nowego kolegę do pracy w naszym biurze. Niestety, przez nadmiar spraw nie cierpiących zwłoki nie mam możliwości wdrażać go “na spokojnie” we wszystkie aspekty/ narzędzia/ triki. Widzę, że niespecjalnie sprawdzam się w edukowaniu go. Z drugiej strony: meneżment ignoruje fakt, że sytuacja jest w tej chwili dość nietypowa i oczekuje, że nasza wydajność będzie taka sama jak wtedy, gdy pracował ze mną człowiek, który gros z tych trików sam wygrzebał (czy to googlem, czy metodą prób i błędów). To potęguje presję, redukuje margines tolerancji na pomyłki (które na początku pracy nie są przecież niczym niezwykłym), generuje spięcia i pogarsza atmosferę w pracy. Tyle o cieniach. Są też jaśniejsze strony.

WSS.pl – światełko w tunelu

Na WSS zarejestrowałem się już jakiś czas temu, ale dopiero ostatnio zacząłem tam regularnie bywać. Parę razy wziąłem udział w dyskusjach na forum, później napisałem krótki artykuł o zdarzeniach WMI. Dodatkowo w przyszłym tygodniu, za namową jednego z redaktorów WSS (Ziemek Borowski: http://ziembor.pl/) będę miał przyjemność podzielić się swoją wiedzą o PowerShellu z tymi, których Microsoft uszczęśliwił wsparciem PowerShella najwcześniej – użytkownikami i specjalistami od Exchange’a. Oprócz tego, również dzięki informacji na WSS wybrałem się wreszcie na spotkanie WGUiSW. Samo spotkanie było całkiem ciekawe, szczególnie kończące je laboratorium o ACLkach, co do których nadal czuję, że wiem za mało. Dodatkowo tam też już sam zgłosiłem się do “Idola”, mam nadzieję że świat się nie zawali i w styczniu będę miał okazję pomarudzić o PowerShellu również przed tym audytorium (choć dokładny temat nie jest jeszcze ustalony, to trudno mi sobie wyobrazić inny zakres niż PowerShell właśnie).

PowerShell Deep Dive ver MTS

Październik zapowiada się nie mniej ciekawie. Jak część z Was pewnie już wie, w tym właśnie miesiącu we Frankfurcie odbywać się będzie The Experts Conference. Podobnie jak jej siostrze w Las Vegas na wiosnę, będzie jej towarzyszyć “PowerShell Deep Dive”. Czym jest ta konferencja, jak bardzo zaawansowane tematy są na niej poruszane można “podejrzeć” na filmach zarejestrowanych właśnie w stolicy hazardu: tutaj. To, czego może się spodziewać we Frankfurcie znaleźć można na stronie konferencji. Tam też można się zarejestrować. Coś, o czym wiecie z pewnością wszyscy to fakt, że w tym samym miesiącu odbywać się będzie w Warszawie MTS. Pechowo złożyło się, że terminy obu konferencji się nakładają. I choć do MTS mam zdecydowanie bliżej, całość wychodzi taniej i w ogóle – to jednak od początku chyba zdecydowany byłem na wycieczkę do Frankfurtu. Znów, bez wsparcia ze strony miłej żonki byłoby to niemożliwe – w pracy oczywiście wszyscy życzą mi powodzenia, ale na wsparcie finansowe tej podróży (czy choćby zwrot kosztów samej konferencji) liczyć nie mogę. Ale chyba nie wybaczyłbym sobie, gdybym tam nie pojechał. Zwłaszcza, że to nie tylko konferencje i prelekcje, ale przede wszystkim dyskusje, wymiana doświadczeń i okazja, by zanurzyć się po pachy w temacie PowerShella. Już teraz nie mogę się doczekać.

Plany na najbliższy czas

Jeśli bozia da rozumu i uda mi się wydłużyć dobę o parę godzin – to zamierzam w najbliższym czasie opublikować serię artykulików o tym jak w PowerShellu (moim zdaniem) poruszać się efektywnie. Jakich rzeczy unikać, jakie starać się na sobie wymuszać. Zamierzam poruszyć temat zaawansowanych funkcji, modułów – tego wszystkiego, co przyniosła ze sobą wersja druga PowerShella, a co czasem jest pomijane. A zdecydowanie może uprościć pracę z tym środowiskiem. Na koniec zamierzam pomarudzić o tak zwanych “najlepszych praktykach”. Będzie to zarówno zbiór tego, co prawią na ten temat inni (o ile się z nimi zgadzam), ale również kilka rzeczy, które sam uważam za właściwe. Postaram się wzbogacić wszystkie te artykuły o odnośniki do miejsc, z których sam czerpałem (i nadal czerpię) wiedzę w tym zakresie. Trzymajcie kciuki i pompujcie ze mną balonik p.t. “doba”. 😉

Typy wyliczeniowe w PowerShellu, część 2

•24 lipca, 2011 • Dodaj komentarz

W pierwszej części opisałem pracę z typami wyliczeniowymi, które w PowerShellu istnieją out-of-the-box. Drugą część będzie poświęcona temu jak i po co moglibyśmy je tworzyć. Zacznę naturalnie od celu.

Po co mi to?

Czasem w PowerShellu chcielibyśmy zagwarantować, że pewne parametry będą przyjmować ściśle określone wartości. Np. typ komputera w naszej firmie to zawsze albo laptop, albo desktop, albo serwer. Narzędzia powinny korzystać tylko z tych wartości, a gdy ktoś spróbuje użyć innej (notebook, stacja robocza…) generować błąd. Można to osiągnąć na dwa sposoby: walidacja parametrów lub właśnie użycie typu wyliczeniowego. Prosta funkcja, która pozwala skorzystać z obu opcji:

function New-Asset {            
[CmdletBinding(DefaultParameterSetName = 'Enum')]            
param (            
    [Parameter(Mandatory = $true,            
        ParameterSetName = 'NoEnum',            
        HelpMessage = 'Typ komputera.')]            
    [ValidateSet('Laptop','Desktop','Server')]            
    [string]$Typ,            
    [Parameter(Mandatory = $true,            
        ParameterSetName = 'Enum',            
        HelpMessage = 'Rodzaj komputera.')]            
    [rodzaj]$Rodzaj,            
    [Parameter(Mandatory = $true,            
        HelpMessage = 'Nazwa komputera.')]            
    [string]$Nazwa            
)            
            
    switch ($psCmdlet.ParameterSetName) {            
        NoEnum {            
            $Script = {            
                [string]$Nazwa = $args[0]            
                [string]$Typ = $args[1]            
                Export-ModuleMember -Variable *            
            }            
            $Arguments = $Nazwa, $Typ            
        }            
        Enum {            
            $Script = {            
                [string]$Nazwa = $args[0]            
                [rodzaj]$Rodzaj = $args[1]            
                Export-ModuleMember -Variable *            
            }            
            $Arguments = $Nazwa, $Rodzaj            
        }            
    }            
            
    $Options = @{            
        ScriptBlock = $Script            
        AsCustomObject = $true            
        ArgumentList = $Arguments            
    }            
            
    New-Module @Options            
}            

Zarówno parametr ‘Typ’ jak i parametr ‘Rodzaj’ mogą mieć tylko założone przez nas wartości. Niestety, ValidateSet nie dba o wielkość liter… Skutek?

clip_image001

Dużo większą kontrolę uzyskamy więc dzięki typom wyliczeniowym. Co jeszcze? ValidateSet byłby w tym wypadku zastosowany jedynie przy tworzeniu obiektu, jeśli skorzystamy z New-Module do tworzenia obiektu mamy możliwość utrwalania typów właściwości. W przypadku typu [string] daje nam to niewiele, ale skorzystanie z typu wyliczeniowego gwarantuje nam, że nawet modyfikując obiekt będziemy zmuszeni użyć właściwych wartości:

clip_image002

I ostatnia rzecz: tworząc cały moduł w oparciu o założenie, że nie pojawi nam się inny typ komputera, prościej jest zrealizować to przy pomocy typów wyliczeniowych. Zmiana “zestawu” też będzie banalna (modyfikujemy definicję klasy, zamiast modyfikować ValidateSet we wszystkich funkcjach). Wydaje mi się, że tych zalet warto czasem sięgnąć po typ wyliczeniowe. Przejdźmy więc do odpowiedzi na pytanie: jak.

Zaprogramujmy sobie typ.

W wersji pierwszej PowerShella nie było prostej metody na przeprowadzenie takiej operacji. W wersji drugiej mamy dostęp do polecenia Add-Type, które pozwala na kompilowanie kodu ‘w locie’. Dodanie klasy [rodzaj], którą użyłem w funkcji wymagało ode mnie wpisanie jednej, niezbyt skomplikowanej linii kodu:

clip_image003

Przy okazji: widać to, o czym pisałem w części pierwszej. Nowostworzona klasa pozwala używać tych samych metod, co jej ‘matka’ System.Enum. Oczywiście nic nie stoi na przeszkodzie, by klasę tą umieścić w jakiejś przestrzeni nazw, lub stworzyć własną przestrzeń, gdzie przechowywalibyśmy wszystkie stworzone przez nas typy wyliczeniowe. Tyle wersja druga PowerShella. W wersji trzeciej możemy oczekiwać jeszcze lepszego wsparcia dla tego typu zabiegów. Cytując Bruce’a Payette’a, członka zespołu tworzącego PowerShell i autora najlepszej, moim zdaniem, książki PowerShellowi poświęconej:

A common question is why the PowerShell team didn’t add some sort of native mechanism to create .NET types in v2. It’s because a) we were incredibly busy and b) we didn’t want to screw up. With the Dynamic Language Runtime (DLR) on the verge of becoming part of .NET (and it is as of .NET 4.0) the way to create types was in flux. Because we had to ship well before .NET 4 was out, we decided to wait one more release and let the dust settle. We’re keeping our fingers crossed that we’ll finally get native type definition capabilities into v3 (actually fingers, toes, eyes, and any other anatomical protuberances that come to mind).

Pozostaje trzymać również trzymać kciuki…? Na szczęście możemy zrobić nieco więcej: w PowerShellu możemy dość łatwo dodać słowo kluczowe ‘enum’, dzięki któremu sami będziemy mogli tworzyć typy wyliczeniowe w sposób analogiczny do tego, w jaki tworzy się je w C#:

function New-Enum {            
param (            
    [Parameter(Mandatory = $true,             
        HelpMessage = 'Name of your enumerator.')]            
    [string]$EnumName,            
    [Parameter(Mandatory = $true,             
        HelpMessage = 'Definition of this type.')]            
    [scriptblock]$Definition,            
    [Parameter()]            
    [ValidateNotNullOrEmpty()]            
    [string]$Namespace            
)            
            
    Set-StrictMode -Version Latest            
            
    $TypeDefinition =  @"
using System;

$(if ($namespace) { "namespace $Namespace {" } )
    public enum $EnumName 
    {
        $($Definition.ToString())
    }
$(if ($namespace) { "}" } )
"@            
            
    Write-Verbose "Type will be defined using: `n$TypeDefinition"            
    try {            
        Add-Type -TypeDefinition $TypeDefinition -ErrorAction Stop            
    } catch {            
        Write-Error "Error creating type. Use -Verbose for more"            
        Write-Verbose $_            
    }                
}            
            
$MyAlias = @{            
    Name = 'enum'             
    Value = 'New-Enum'            
    Description = 'Keyword to create new System.Enum types'            
}            
New-Alias  @MyAlias            

Od tej pory korzystając z aliasu ‘enum’ i parameterów na odpowiednich pozycjach możemy napisać wprost w konsoli:

clip_image004

Myślę, że mając typy wyliczeniowe na wyciągnięcie ręki trudno się będzie powstrzymać przed ich użyciem. Korzyści może nie są wielkie, ale z pewnością w wielu sytuacjach oszczędzą nam bólu głowy. Choćby dlatego, że pomagają wyeliminować w zasadzie najgorsze w debugowaniu błędy: literóweczki… 😉

Typy wyliczeniowe w PowerShellu, część 1

•21 lipca, 2011 • 3 Komentarze

Początkowo planowałem wszystko opisać w jednym artykule, ale niestety rozrosło się to znacznie. Więc w pierwszej części skupię się na wykorzystywaniu typów wyliczeniowych w PowerShellu, a tworzenie ich opiszę następnym razem.

Z czym to się je?

Nie jestem programistą, jednak praca z PowerShellem siłą rzeczy wymusza rozumienie choć podstaw platformy .NET. Typy wyliczeniowe są jej częścią i zdecydowanie ułatwiają pracę w PowerShellu. Prosty przykład: czy łatwiej (zwłaszcza na początku) zapamiętać, że aby wyłączyć raportowanie błędów należy użyć wartości dla ErrorAction ‘SilentlyContinue’, czy wolelibyście zawsze używać wartości numerycznej 0? Dziś chcę poświęcić im nieco czasu: gdzie można je znaleźć, jak sprawdzić jaką mają wartość w części pierwszej. W drugiej napiszę jak zdefiniować własne typy wyliczeniowe i czemu moglibyśmy chcieć to zrobić.

Gdzie je znajdziemy?

Typy wyliczeniowe wykorzystujemy w (niemal?) wszystkich komendach (choćby z tego powodu, że parametry *Action wykorzystują typ wyliczeniowy System.Management.Automation.ActionPreference). Aby uzyskać pełną listę najlepiej skorzystać z samego PowerShella. Wykorzystamy do tego Get-Command i kolekcję Parameters, która jest jedną z właściwości obiektów przez tą komendą generowanych. Widać tam typ parametru, a każdy typ posiada właściwość logiczną IsEnum. Kod poniżej:

Get-Command -CommandType cmdlet -Name * |             
    ForEach-Object { $_.Parameters.GetEnumerator() } |             
    Sort-Object -Unique Key |             
    Select-Object -ExpandProperty Value |             
    Where-Object { $_.ParameterType.IsEnum } |             
    Select-Object Name, ParameterType, @{            
        Name = 'CommandName'            
        Expression = {              
            @(Get-Help -Name * -Parameter $_.Name)[0].Name             
        }            
    }            

Na moim komputerze znalazłem 31 takich parametrów. Większość korzysta z unikalnej klasy, więc by z nich skorzystać należałoby poznać możliwe wartości.

A imię jego…

Sposobów na znalezienie wartości ‘opisowych’ jest kilka. Zacznijmy od najprostszej. Wykorzystamy do tego fakt, że informacja o błędzie dotyczącym parametru będącego typem wyliczeniowym jest jednocześnie doskonałym źródłem informacji o tym, jakie wartości prawidłowe:EnumErrorOczywiście bez try/catch to też zadziała, użyłem go tylko po to, by ładnie ‘złamać’ wyświetlaną informację w konsoli. 😉 Istotne części dodatkowo podkolorowałem. A istotne są wartości prawidłowe (których, dzięki takim trikom, nie musimy się uczyć na pamięć), oraz typ używany (który przyda nam się za chwilkę).

Druga opcja to sprawdzić typ wykorzystywany (np. zagłębiając się w rezultat Get-Command) i skorzystać ze statycznych metod typu wyliczeniowego:EnumGetNames Wykorzystałem klasę „matkę” (System.Enum), ale równie dobrze mogłem użyć każdej innej, wszystkie dziedziczą metody statyczne Get*. Metody te pozwalają też uzyskać informację, jaką nazwę przypisano danej wartości numeryczneEnumGetNamePrzeglądając metody statyczne klasy [enum] trudno nie zauważyć metody GetValues. Na logikę spodziewać by się można, że dzięki temu zobaczymy wartości liczbowe. I tak też będzie, mimo że na pierwszy rzut oka wynik metod GetNames i GetValues będzie identyczny.

Tam sięgaj gdzie wzrok nie sięga.

Przyczyną tego, że rezultat GetValues może być na pierwszy rzut oka mało ciekawy jest fakt, że PowerShell domyślnie wykorzystuje metodę ToString() typów wyliczeniowych, a ta zwraca ich nazwę. Skoro nie widać różnicy… to trzeba skorzystać z czegoś, co różnicę uwypukli. Na pierwszy ogień – Get-Member:EnumGetMemberMetody, jak widać, nie są zbyt ciekawe. A jedyna właściwość to value__. Nie trudno odgadnąć, że to tam właśnie jest zapisana wartość numeryczna typu wyliczeniowego. Wiedząc jaka ta wartość jest, możemy użyć jej zamiast nazwy. Pracując z PowerShellem interaktywnie oszczędzimy sobie sporo czasu:

# Uzyjemy:            
ls nonexistent -EA 0            
# Zamiast:            
ls nonexistent -ErrorAction SilentlyContinue                

Przy pomocy prostej funkcji możemy uzyskać pełnię informacji o wartościach parametru komendy, gdy jest on typu wyliczeniowego: nazwę i odpowiadającą jej wartością numeryczną:

function Get-ParameterValues {            
param (            
    [ValidateScript( { Get-Command $_ })]            
    [string]$Command,            
    [string]$Parameter            
)            
            
$CommandInfo = Get-Command $Command            
if ($type = $CommandInfo.Parameters.$Parameter.ParameterType) {            
    if ($type.IsEnum) {            
        [enum]::GetValues($type) | ForEach-Object {            
            New-Object PSObject -Property @{            
                Name = $_.ToString()            
                Value = $_.value__            
            }            
        }            
    }            
}            
}

W skryptach, dla przejrzystości, najlepiej oczywiście używać znaczących i zrozumiałych nazw. W czasie pracy interaktywnej – można skorzystać z wartości numerycznych. Ewentualnie, korzystając z modułu PowerTab, dopełniać wartości takich parameterów przy pomocy [TAB].

Profile – część szósta.

•14 lipca, 2011 • 1 komentarz

W ostatniej części cyklu chcę skupić się na bezpieczeństwie profili. To, co jest wielką zaletą profili, może się jednocześnie stać potencjalnym źródłem niebezpieczeństwa. Profil jest domyślnie uruchamiany razem z hostem, do którego jest przypisany. Oprócz tego większość hostów uruchamia profil przypisany do danego użytkownika (lub do wszystkich użytkowników) niezależnie od hosta, którego aktualnie używamy. Bez tego nie miałyby one wielkiego sensu. I o ile profile dotyczące wszystkich użytkowników wymagają pełnych uprawnień na danym komputerze, o tyle profil bieżącego użytkownika nie jest w żaden sposób zabezpieczony – z punktu widzenia systemu niczym nie różni się od wszystkich innych plików przechowywanych w folderze z dokumentami. W dodatku jego nazwa jest stała (nie ma sposobu, by nadpisać domyślną ścieżkę). To wszystko czyni profil użytkownika idealnym punktem, w który można by uderzyć atakiem ‘wstrzykującym’ kod. Prosty przykład:

<# To będzie wyglądało jak komentarz blokowy dla niewprawnego oka
    #>Import-Module ActiveDirectory                        
    Get-ADUser -Filter * | Remove-ADUser -Force <#            
    A to drugi komentarz, który wyglada jak dokończenie pierwszego.            
#>

Raczej nie chciałbym, by ktoś taki „komentarz” dopisał na końcu profilu, uruchamianego na koncie z uprawnieniami domain admin. Na szczęście nie wymaga wiele pracy zabezpieczenie się na taką ewentualność. Lekarstwem na to potencjalne zagrożenie jest podpis elektroniczny.

Złoty środek.

Podpis elektroniczny wymaga nieco pracy. Trzeba go stworzyć lub zakupić. Każda modyfikacja skrypty podpisanego elektronicznie wymaga ponownego podpisania skryptu. Czy chcielibyśmy wobec tego pracować cały czas z Execution Policy AllSigned, nawet jeśli świadomie uruchamiamy skrypty, zabezpieczone bardziej niż zawartość naszego folderu z dokumentami i przez to nie narażonymi na ‘wstrzykiwanie’ kodu? Z drugiej strony, jeśli chcemy mieć pewność, że nikt nie będzie mógł w prosty sposób zmodyfikować naszych profili, to nie specjalnie mamy wybór. Długo nie mogłem znaleźć wyjścia gdzie i wilk byłby syty, i owca cała… Z pomocą, jak zawsze, przyszła wspólnota. Glenn Sizemore jakiś czas temu opisał metodę, która pozwala skorzystać zarówno z dobrodziejstwa jakie daje wymuszenie podpisu w profilach a jednocześnie swobodną pracę „interaktywną”, nie zmuszającą do podpisywania każdego skryptu, z którym przyjdzie nam pracować (czy raczej – nad którym przyjdzie nam pracować). Schematycznie można to przedstawić w ten sposób:

image

Zmiana ExecutionPolicy jest możliwa z tej prostej przyczyny, że komenda

Set-ExecutionPolicy, o czym wiele osób zapomina, ma parametr Scope. Po pierwsze zawężenie Scope do CurrentUser pozwala zmodyfikować ustawienia bieżącego użytkownika, nawet jeśli nie ma on uprawnień administratora. Po drugie, ustawienie Scope  na pojedyńczy Process pozwala ograniczyć zmianę tylko do bieżącego procesu, co otwiera furtkę dla tego właśnie rozwiązania. Pełna komenda ma postać:

$Parametry = @{            
    Scope = 'Process'            
    ExecutionPolicy = 'RemoteSigned'            
    Force = $True            
}            
Set-ExecutionPolicy @Parametry

Implementacja

Zdobycie podpisu elektronicznego to temat na osobny artykuł, pozwolę więc sobie pominąć dziś ten etap. Zakładam, że podpis takowy już posiadamy. Druga rzecz, jaką powinniśmy zrobić, to zmodyfikować ExecutionPolicy (ustawić je na AllSigned). Kolejna – to oczywiście podpisanie wszystkich posiadanych profili. Ostatnia: zmodyfikowanie profili dla każdego z używanych hostów w taki sposób, by komenda zmieniająca ExecutionPolicy się tam znalazła. Naturalnie, można umieścić ją na samym końcu, ale często może się okazać, że spowoduje to znaczne problemy przy ładowaniu modułów. Można więc albo również je podpisać (czasami wymaga to sporo pracy), lub też przesunąć komendy ładujące moduły nieco dalej. Wydaje mi się, że nie ma to wielkiego wpływu: i tak dopisanie modułu będzie wymagało ponownego podpisania całego profilu, nie powinno więc stanowić zagrożenia. Osobiście, dla ułatwienia sobie pracy, dodałem jeszcze do profilu prostą funkcję, którą aktualizuję podpis elektroniczny wybranego skryptu, przy pomocy wykorzystywanego przeze mnie certyfikatu:

function Update-scriptSignature {            
            
<#
    .Synopsis
        Function to update script SIG if not yet valid/ signed.
    .Description
        Function to simplify work with Set-AuthenticodeSignature. 
        Using Get-AuthenticodeSignature to verify script first.
        By default it will use first CodeSigning cert found on PC.
        Primary purpose is to update certificate for profile.ps1.
    .Example
        $profile | Update-ScriptSignature
        Updates signature for profile for current host.
    .Example
        Update-ScriptSignature
        Without any parameter it will update certificate for 
        $profile.CurrentUserAllHosts
    .Inputs
        System.String, System.IO.FileInfo
    .Outputs
        System.Management.Automation.Signature, 
        
#>            
            
[CmdletBinding()]            
param (            
    # Path to file that will be signed            
    [Parameter(ValueFromPipeline=$true,            
        ValueFromPipelineByPropertyName = $true)]            
    [Alias('Path','FullName')]            
    [string]$FilePath = $profile.CurrentUserAllHosts,            
    # Certificate object that will be used to sign a script            
    $Certificate = $(Get-ChildItem cert:\CurrentUser\My -CodeSigning |            
        Select-Object -First 1)            
)            
            
begin {            
    $Arguments = @{            
        Certificate = $Certificate            
        ErrorAction = 'Stop'            
    }            
}            
            
process {            
    Write-Verbose "Updating certificate for file: $FilePath"            
    try {            
        $Signature = Get-AuthenticodeSignature -FilePath $FilePath            
        if (!$Signature -or $Signature.Status -ne 'Valid') {            
            $Arguments.FilePath = $FilePath            
            Set-AuthenticodeSignature @Arguments            
        } else {            
            Write-Host $FilePath "has already a valid cerificate"            
        }            
    } catch {            
        Write-Verbose "Errors while processing file: $FilePath"            
        $_ | Format-List * -Force            
    }            
}            
            
}            

Używam jej w zasadzie głównie wtedy, gdy potrzebuję zmodyfikować swój profil. Jeśli zapomnę z niej skorzystać, PowerShell zachowa się tak, jak zachowa się gdy coś zechce zmodyfikować mój profil i dopisać kilka “niewinnych” linii:

AllSignedverRemoteSigned

Jak widać po prawej, gdy wymusiłem nieco mniej restrykcyjne ExecutionPolicy (powershell.exe –ExecutionPolicy RemoteSigned) mój “groźny” kod został wykonany. W normalnych warunkach – nic takiego nie miało miejsca. Myślę, że to dobry sposób, by ustrzec się przed najprostszymi próbami wykorzystania profili przeciw nam. Sam korzystam z tego rozwiązania i zapewniam, nie jest bardzo uciążliwe, a sen mam dzięki temu nieco spokojniejszy… 😉 I tym miłym akcentem kończę cykl o profilach. Następny post będzie poświęcony enumeratorom w PowerShellu.

Profile – część piąta.

•21 czerwca, 2011 • Dodaj komentarz

Poprzednie części:

Część 4: PowerShell ISE

Część 3: PowerShell.exe

Część 2: Co dodać do profili

Część 1: Co to jest profil, rodzaje profili

W części piątej cyklu o profilach skupię się na innych programach, które wspierają obsługę profili. Aby wsparcie rzeczywiście było możliwe (poza wspieraniem rzeczy od hosta w zasadzie niezależnych, jak ładowanie modułów, definiowanie funkcji, aliasów, zmiennych…) niezbędny jest dostęp do samej aplikacji z poziomu konsoli. Obecnie wiem o dwóch narzędziach (poza PowerShell ISE, o którym pisałem ostatnio), które taki dostęp dają: PowerGUI i PowerShell Plus.

PowerGUI i $pgse

Model obiektowy w PowerGUI jest dość silnie rozbudowany, w zasadzie obecnie nie odbiega zbytnio od tego, który istniał od jakiegoś czasu w ISE (jak wiadomo, ISE od momentu wypuszczenia PowerShella 2.0 nie uległo żadnym zmianom, cykl życia poszczególnych wersji PowerGUI jest zdecydowanie krótszy, był więc czas by „nadgonić”). To stało się podstawą do stworzenia modułu, który umożliwia tworzenie dodatków do obu edytorów (ISE i PowerGUI): SparkPlug. Jego autor, James Brundage, był też autorem jednego z pierwszych dodatków do PowerShell ISE (ISEPack), którego zresztą SparkPlug jest kontynuacją (wiele funcji pokrywa się z tymi, które były w ISEPacku).

Jest jednak rzecz, która konsolę PowerGUI odróżnia. Kolorki… 🙂 Konsola w PowerGUI umożliwia praktycznie dowolne konfigurowanie kolorystyki w oparciu o pełną gamę z przestrzeni nazw System.Drawing. Nic więc nie stoi na przeszkodzie by wyświetlić “tęczę”:

$Colors = $PGSE.Configuration.Colors.Console            
$Colors.ForegroundColor = 'Black'            
foreach ($color in [Enum]::GetNames([System.Drawing.KnownColor])) {            
        $Colors.ForegroundColor = $color            
        Write-Host $color            
}            

Naturalnie, każdy z tych kolorów może być wykorzystany do tła/ tekstu w przypadku wystąpienia informacji error/ warning/ verbose/ debug, jak i „normalnych” informacji wyświetlanych w oknie konsoli wbudowanej w edytor skryptów. Profilem możemy się więc posłużyć zarówno do utworzenia „skrótu” do kolorków, jak i ustawienia kolorów w sposób nam odpowiadający. Przy okazji: pętla ta przydaje się do wygenerowania ściągi przed rozmową z ładniejszą połową świata na temat kolorów… 😉

Obiekt $pgse istnieje domyślnie w wersji PowerGUI 3.0, można go utworzyć w PowerGUI 2.4. Dla bezpieczeństwa (jeśli chcemy mieć pewność, że taki obiekt istnieje) możemy na samym początku profilu dodać:

try {            
 if (!$PGSE) {            
  $PGSE = [Quest.PowerGUI.SDK.ScriptEditorFactory]::CurrentInstance            
 }            
 # Some operations with $PGSE, such as colours setup.            
} catch {            
 Write-Warning '$PGSE could not be created.'            
}

Wszystkie te zmiany najlepiej wprowadzić do profilu związanego z PowerShell.exe, który jest traktowany przez PowerGUI jak $profile.CurrentUserCurrentHost, choć host „nieco” się różni.

PowerShell Plus i $psplus

W wersji 4 PowerShellPlus pojawił się obiekt $psplus, który w następnych wersjach tego produktu ma urosnąć. W chwili obecnej jest dość skromny i nie daje zbyt dużej elastyczności:

<#PS [7] #> $psplus | Format-Custom            
            

class HostController
{
  AutoAdjustSize = True
  AutoTriggerEnabled = True
  CursorSize = 100
  FontFace = Consolas
  FontSize = 16
  LastPipeResult =
  MiniMode = False
  Opacity = 1
  PSCredential =
  PSVersion =
  (…)
  PublicName = psplus
  QuickEditMod
}

W zasadzie przydatne opcje to czcionka (typ i wielkość), opcja MiniMode, przezroczystość, Quick Edit Mode. Nic naturalnie nie stoi na przeszkodzie, by z opcji tych skorzystać i jeśli używamy PowerShell Plus często – na pewno docenimy fakt, że przenosząc profil na inną maszynę, będziemy mogli dostosować te parametry do naszych potrzeb. Trudno jednak porównywać tę funkcjonalność z tym, co oferuje w tym zakresie PowerGUI i PowerShell ISE.

Reaktywacja…

•4 czerwca, 2011 • 5 Komentarzy

Minęły dwa długie miesiące od mojego ostatniego posta. Miesiące wypełnione po brzegi niesamowitymi zdarzeniami, wszystko to – dzięki Scripting Games, a ponieważ w tym roku opierały się one jedynie na skryptach w PowerShellu – to właśnie mojemu zamiłowaniu do tej technologii zawdzięczam wszystko, co wydarzyło się od 4 kwietnia. Do serii o profilach wrócę więc wkrótce, dziś opiszę to wszystko co spowodowało, że ostatnio zamilkłem. 😉 Na początek fotka, ciekawe czy rozpoznacie dwóch panów w środku…? 🙂

TechEd_IMG_7765

To taki ‘appetizer’. 😉 Reszta – niżej.

Scripting Games: 4 – 25 kwietnia

Przez prawie cały kwiecień każdą wolną chwilę poświęcałem tej imprezie. Najpierw (4 – 15 kwietnia) publikowano wszystkie zadania. Oczywiście jednocześnie można było zadania rozwiązywać, jednak ostateczny termin dla każdego z zadań wyznaczony był tydzień po jego ogłoszeniu. Czyli ostatnie zadanie zostało “zamknięte” 22 kwietnia, sam posłałem jakos ostatnie zadanie ósme, najtrudniejsze. Oprócz tego w tym roku nie było możliwości przeglądania skryptów innych uczestników podczas trwania konkurencji, dopiero po jej zakończeniu można było przejrzeć i skomentować skrypty napisane przez innych, co starałem się dość mocno eksploatować. Najgorsze przyszło na koniec – niecierpliwe oczekiwanie na ostateczny wynik. Koniec końców zająłem pierwsze miejsce, co oznaczało między innymi wstęp na TechEd w Atlancie…

Przygotowania do podróży: 26 kwietnia – 6 maja

Jeszcze tego samego dnia pochwaliłem się tym niewątpliwym sukcesem mojej ładniejszej połowie. Jej reakcja nieco mnie zaskoczyła. Od początku namawiała mnie na wyjazd do Atlanty, łącznie z propozycją, by koszta – jeśli w pracy nie uzyskam wsparcia – pokryć z naszych oszczędności. Od zawsze wiedziałem, że mam wyjątkową żonę, ale nie sądziłem, że aż TAK wyjątkową. 🙂 Wyszukałem odpowiednie połączenie, zacząłem załatwiać wizę, przy okazji jakieś zakupy… Było z tym nieco przygód, ale ostatecznie wszystko udało się załatwić. I choć szef szefa mojego szefa nie zgodził się na oficjalne opłacenie kosztów podróży czy hotelu, to jednak z firmy uzyskałem znaczne wsparcie finansowe. Na nic by się ono jednak nie zdało, gdyby nie nasze oszczędności (za podróż musiałem zapłacić sam, a dopiero później część kosztów została mi w taki czy inny sposób zwrócona) i stosunek do całej inicjatywy mojej miłej żonki. 4 maja spotkałem się z pracownikiem ambasady w sprawie wizy, zgodę uzyskałem, a już 6 maja późnym popołudniem do biura dotarł mój paszport. Pozostał już tylko tydzień…

Szkolenie z SCCM – Almado: 9 – 13 maja

Dość długo czekałem na to szkolenie i mocno go wypatrywałem. Wcześniej uzyskałem informację, że prowadzić je będzie Marcin Kozakiewicz, co z mojego punktu widzenia jest doskonałą wieścią. Ostatecznie szkolenie poprowadził Piotr Młynarczyk. I choć z początku, nie ukrywam, byłem nieco zawiedziony, to szybko przekonałem się, że Almado to po prostu jakość sama w sobie, każdy trener wykłada w sposób zarówno interesujący, kompetentny jak i nieco bardziej elastyczny i “życiowy” niż nakazywałyby podręczniki MS. Dla mnie to wielka jakość dodana. Niestety, nie mam już więcej koncepcji szkoleń, na które mógłbym się tam wybrać…

TechEd: 15 – 19 maja

Nigdy wcześniej nie uczestniczyłem w TechEd. Od kilku lat regularnie oglądam sesje nagrywane w jego trakcie, więc po  części wiedziałem czego się spodziewać. Ale być tam, a być tam jako zwycięzca Scripting Games – to chyba dwie odrębne sprawy. Miałem okazję poznać osobiście twórców PowerShella (Jeffrey Snover, Jason Shirk, Mir Rosenberg, Dan Harman), MVP – z jednym z nich, Aleksandarem Nikolicem z Serbii dzieliłem pokój w hotelu, oczywiście Scripting Guy(s) – czyli de facto Eda Wilsona, jego żonę (Scripting Wife), twórców PowerScripting Podcast i wiele, wiele innych osób, których spotkanie w innych okolicznościach byłoby praktycznie niemożliwe. Przywiozłem kilka książek z autografami, kilka zdjęć i całą masę wspomnień. Miałem też przyjemność uczestniczyć w tzw. PowerShell Dinner, który odbywa się przy okazji każdej konferencji tego typu. I wierzcie mi, darmowe żarcie było w tym spotkaniu najmniej ważnym bonusem. 😉 Zdjęcie na początku było zrobione właśnie w jego trakcie. Od prawej widać na nim Glenna Sizemore’a (zwycięzcę Scripting Games 2010), Eda Wilsona “The Scripting Guy”, Jeffrey’a Snovera i mnie. Myślę, że Ed i Jeffrey byli zaszczyceni… 😉

Odcinanie kuponów: 20 maja – dziś 😉

Jakby tego wszystkiego było mało: dodatkowo po raz pierwszy mój post został opublikowany na blogu ScriptingGuys. Ed zaprosił mnie też do grona moderatorów na oficjalnym forum ScriptingGuys, co dla mnie przynajmniej oznacza nie lada wyróżnienie. I wreszcie – za tydzień spodziewam się kolejnego posta ‘gościnnego’ na blogu Hey, Scripting Guy!, wstępnie planowany jest na 12 czerwca. Myślę, że taki ogrom wydarzeń mógłby przytłoczyć każdego. Stąd też brak kolejnych wpisów na blogu w ostatnim czasie. Ale obiecuję się poprawić i teraz dość regularnie publikować wpisy o PowerShellu i wokół PowerShella oscylujące.

Profile – część czwarta.

•30 marca, 2011 • 4 Komentarze

Jako miłośnik rozwiązań “wszystko w jednym” jestem gorącym zwolennikiem pracy w PowerShell ISE. ‘Normalną’ konsolę odpalam sporadycznie, na ogół po to, by przetestować rozwiązania pisane w ISE. Host ten do niedawna dawał wyjątkową możliwość rozbudowywania narzędzia ‘od środka’. Przy pomocy zmiennej $psISE można dodawać menu, otwierać i zmieniać zawartość plików, zmieniać opcje kolorystyczne, rozmieszczenie paneli, wielkość czcionki itd, itp. Obecnie kolejne hosty zaczynają oferować podobne rozwiązania (prym wiedzie w tym darmowe PowerGUI), ja jednak pozostałem wierny PowerShell ISE. Może to sentyment, może przyzwyczajenie (które jak wiemy jest drugą naturą człowieka). W związku z możliwością rozbudowy i częstotliwością używania mój profil w ISE rósł w oczach. Wkrótce okazało się, że część funkcjonalności zaczęła żyć własnym życiem – wtedy to zdecydowałem się tę część upublicznić i zamknąć w module. ISEFun miało być po prostu zestawem funkcji często przeze mnie w ISE wykorzystywanych (jedną z nich już prezentowałem – Edit-Function), ale ostatecznie zaczęło się to rozrastać na cały zestaw narzędzi, przy pomocy których upraszczałem sobie pracę w ISE. Ten host ma jednak dla mnie przede wszystkim inną zaletę. Mogę w nim utworzyć kilka niezależnych ‘konsol’ – każda ze swoim zestawem zmiennych, załadowanych modułów, historię poleceń, kolekcję otwartych plików… Łączy je $psISE. I tu zaczyna się zabawa.

Szybki start i bogactwo narzędzi.

PowerShell, gdy go ciągle rozbudowujemy o kolejne dodatki, staje się coraz wolniejszy. Uruchamia się długo, a czasem chcemy wykonać jakieś proste polecenie szybko, nie tracąc czasu na ładowanie wszystkich dodatków. I rodzi się dylemat: jak tu zjeść ciastko i mieć ciastko? ISE daje możliwość problem ten obejść. Wystarczy prosty manewr: dwa ‘okienka’ na starcie. Jedno ‘gołe’, drugie w tle ładuje wszystko co nam do szczęścia potrzebne. Po uruchomieniu moje ISE wygląda tak:

ISE Zakładka ‘NoProfile’ jest golutka i startuje szybciutko, zaraz też uruchamia drugą zakładkę, PoSh # 1, gdzie zaczynają się ładować dodatki (między innymi moje ISEFun). W tym czasie ISE wraca grzecznie na pierwszą zakładkę. Mogę działać. 🙂 A gdy druga zakładka zrobi swoje – mogę robić wszystko to, czego bez wtyczek robić się nie da. Dodatkowo tworzę sobie menu, gdzie figurują wszystkie moje zakładki (mogę sie między nimi przełączać skrótek klawiszem ALT + nr zakładki). Reszta mojego profilu to już w zasadzie kosmetyka (lubię czerń, jak widać na załączonym obrazku 😉 ) i ładowanie modułów. W tym mojego własnego, ISEFun, któremu poświęcić zamierzam osobny artykuł. Mój profil, który robi dla mnie te kilka milutkich usprawnień wygląda tak:

if ($curtab -eq $tabs[0]) {            
    $curtab.DisplayName = 'NoProfile'            
    $newTab = $tabs.Add()            
    } else {            
    function prompt {            
        $curtab.DisplayName = "PoSh # $($tabs.IndexOf($curtab))"            
        $(if (test-path variable:/PSDebugContext) {             
            '[DBG]: '             
        } else {             
            ''             
        }) + 'PS ' + $(Get-Location) +             
        $(if ($nestedpromptlevel -ge 1) { '>>' }) + '> '            
    }            
    $curtab.DisplayName = "PoSh # $($tabs.IndexOf($curtab))"            
    $NewTabIndex = $tabs.Count            
    $SwitchToMe = [ScriptBlock]::Create(            
        "`$tabs.SetSelectedPowerShellTab(`$tabs[$($NewTabIndex-1)])"            
        )            
    for ($i = 0; $i -lt $tabs.Count - 1; $i++) {            
                    
        if ($NewTabIndex -le 9) {            
            $SwitchTo = [ScriptBlock]::Create(            
                "`$tabs.SetSelectedPowerShellTab(`$tabs[$i])")            
            $MyMenu = $tabs[$i].AddOnsMenu.Submenus |             
                Where-Object { $_.DisplayName -eq 'Tabs' }            
            try {            
                [void] $Mymenu.Submenus.Add($curtab.DisplayName,            
                    $SwitchToMe ,            
                    "ALT + $NewTabIndex")            
            } catch {            
                Write-Debug 'This menu item is already present!'            
            }            
        } else {            
            # That should not happen cause limit is 8            
            Write-Host -ForegroundColor Red "Can not add this tab"            
        }            
        [void] $TabsMenu.Submenus.Add($tabs[$i].DisplayName,             
            $Switchto,             
            "ALT + $($i+1)")            
    }            
    $tabs.SetSelectedPowerShellTab($tabs[0])            
    $tabs[0].CommandPane.Focus()            
    Add-PsSnapin Quest*            
    Import-Module ('WPK IsePack Inv ISEFun Reflection PSCX'.Split())            
    . "C:\Program Files\Quest Software\Management Shell for AD\qsft.ps1"            
}            

Jak widać nie jest przesadnie rozbudowany. Całe bogactwo rozszerzeń to trzy ostatnie linie. Byłaby jedna, gdybym wreszcie znalazł chwilkę i korzystająć z modułu Reflection ‘przepisał’ narzędzia Questu do modułu. Reszta odpowiada za stworzenie na początku dwóch ‘zakładek’, a przy tworzeniu kolejnych – za modyfikację menu ‘Tabs’ –> dodaje nowoutworzoną zakładkę do pozostałych, i na odwrót – pozostałe w nowoutworzonej. Całość poprzedza jeszcze kilka linii tworzących ‘skróty’ do elementów $psISE i wyżej wspomniana kosmetyka:

$Host.UI.RawUI.WindowTitle = "Only {0} days to Scripting Games!" -f             
    (New-TimeSpan -End '4/4/11').Days            
$tabs = $psISE.PowerShellTabs            
$curtab = $tabs[$tabs.Count-1]            
$ISEOpt = $psISE.Options            
            
$ISEOpt.OutputPaneBackgroundColor = 'black'            
$ISEOpt.OutputPaneTextBackgroundColor = 'black'            
$ISEOpt.OutputPaneForegroundColor = 'white'            

No i naturalnie linia przypominająca mi o tym, że Scripting Games 2011 zbliżają się nieubłaganie… 🙂 Ale to element, który już wkrótce zniknie. Jeszcze tylko 4 dni. 😉

Profile – część trzecia.

•18 marca, 2011 • 2 Komentarze

W tej części zamierzam poznęcać się nieco nad profilem dla ‘tradycyjnej’ konsoli. PowerShell.exe dziedziczy wszystkie ograniczenia cmd.exe. Nawet jego kolorystyka jest w zasadzie mocno naciąganym szachrajstwem. By się o tym przekonać wystarczy sprawdzić jaką wartość mają kolory. Czy Waszym zdaniem piszecie na żółto po fioletowym tle? Nie? To sprawdźcie wartość Back/ Foreround color. Wartości obu parametrów domyślnie ustawione są pewnie tak jak u mnie:

PowerShell_exe Nie regulujcie odbiorników. Wiem, u Was wygląda to zdecydowanie inaczej. Przyczyna? Domyślny skrót do PowerShell.exe ma pozmienianie wartość RGB kolorów podstawowych, więc DarkMagenta może i jest nawet Dark, ale na pewno daleko mu do Magenta. O zażółceniu DarkYellow nawet nie wspominam… 😉

To powoduje, że staram się unikać definiowania kolorystyki konsoli. Skupiam się na jej rozmiarze okna, bufora i funkcji prompt. Zacznę od ostatniego, jako że nie jestem autorem tak zacnej implementacji. Generalnie moja funkcja została zadoptowana ze skryptu autorstwa MVP Joela Benneta (@Jaykul): http://poshcode.org/2095

Korzyści? Ścieżka (wraz z informację o bieżącej przestrzeni nazw) jest wyrzucona do tytułu okna. Widzę, na jakim koncie (i z jakimi uprawnieniami) pracuję. A dzięki temu, że wiem jakie ‘id’ ma bieżąca komenda, mogę bez trudu skorzystać później z cmdletu Invoke-History (alias: r) i ponowić jej wykonanie. Dodatkowo taki sposób wyświetlania ułatwia kopiowanie zawartości do dowolnego edytora skryptów. Prompt jest oznaczony jako komentarz blokowy, nie wpływa na działanie komend wpisanych za nim. 🙂

Pozostaje kwestia gabarytów. Jak wszyscy wiemy skalowanie okna cmd nie jest bardzo przyjemne. W powershell.exe sprawa jest nieco prostsza: zmienna $host zawiera pewne elementy, które umożliwią nam w prosty sposób okno ‘rozdymać’ do rozmiarów maksymalnych. Zwrócę tylko uwagę, że całość wrzuciłem w blok try {} catch. O tym dlaczego tak zrobiłem – nieco niżej. Kod, który obecnie wykorzystuję:

try {            
 $host.UI.RawUI.CursorSize = 100            
 $MySize = $Host.UI.RawUI.MaxPhysicalWindowSize            
 $MySize.Height -= 2 # We need titlebar after all             
 $MySize.Width -= 4            
 $MyBuffer = $MySize            
 $MyBuffer.Height = 5000 # Set to any value you need I like it looong.              
 $Host.UI.RawUI.BufferSize = $MyBuffer            
 $Host.UI.RawUI.WindowSize = $MySize            
} catch {            
 # Since it's PowerGUI, let us set some Console settings...            
 $Host.UI.RawUI.ForegroundColor = 'White'            
 $Host.UI.RawUi.BackgroundColor = 'black'            
}            

W ten sposób okno powinno nam urosnąć, a bufor wydłużyc się znacznie (dzięki temu rezultaty poszczególnych komend jeszcze długo będzie można podejrzeć). Dlaczego przewiduję błędy? Otóż niektóre narzędzia firm trzecich (PowerGUI, PowerShellPlus) mogą uruchamiać nasze profile, w tym profil powershell.exe. Naturalnie, próba zmiany rozmiaru okna w taki sposób nie zadziała w tych aplikacjach. A po co się denerwować błędami? 😉

Co jeszcze w moim profilu do powershell.exe można znaleźć? Niewiele. Z modułów ładuję w tym profilu przede wszystkim PowerTab, który zdecydowanie ułatwia pracę z tabulatorem. Oprócz tego (zakładając ładowanie profilu przez PowerGUI) definiuję kilka elementów przydatnych w zagnieżdżonej w tej aplikacji konsoli (ale to już nieco inna historia…). Do tego kilka innych modułów, których działanie upraszcza pracę z PowerShellem. Część z nich używam zarówno w PowerShell.exe jak i w ISE. Czemu więc nie przeniosłem ich do profilu dzielonego przez wszystkie hosty? O tym w następnej części cyklu, gdy zajmę się cudami jakie można wyczyniać w ISE.

Profile – część druga.

•16 marca, 2011 • 1 komentarz

W pierwszej części tego cyklu stworzylismy pracowicie pusty profil, teraz nadeszła pora, by zacząć go wypełniać treścią. Profil może pełnić z całą pewnością masę funkcji, ale jest kilka takich, do których jest wręcz stworzony:

  • definiowanie: zmiennych, aliasów, funkcji
  • ładowanie modułów i wtyczek
  • konfigurowanie środowiska (kolorków, rozmiarów…)

Jak widać potencjalnych możliwości jest sporo. Pytanie tylko: jak daleko się w tym posunąć? Pamiętajmy, że profile będą się ładować zawsze (no… prawie – możemy w powershell.exe i kilku innych hostach wyłączyć ich uruchamianie). Moim zdaniem najwygodniej jest umieścić w profilu tylko to, czego używamy zawsze. To, co w ogóle zmienia nasz sposób pracy z powłoką. To, bez czego jesteśmy niby ryby wyrzucone na brzeg. Więc na pewno znaleźć się tu powinny wszelkie zmiene, których obecność jest nam niezbędna do pracy. Wszystkie aliasy, do których jesteśmy przyzwyczajeni. Wszystkie funkcje, dzięki którym możemy normalnie pracować… Itd, itp, a finał znamy wszyscy. Przez to mamy zawsze masę zbędnych rzeczy poupychanych w szafach, piwnicach i garażach. Jak tego uniknąć? Kilka propozycji:

  • od czasu do czasu przeglądać profile i rzeczy używane rzadziej ‘wyrzucać’ do modułu o wszystko-mówiącej-nazwie (np. Profil-Mega-Kobyla)
  • jeśli coś używamy bardzo rzadko – zamiast tworzyć funkcję w profilu stwórzmy skrypt i umieśćmy go w jednym z folderów figurujących w naszym $env:PATH
  • jeśli jakieś rzeczy używamy bardzo często, ale tworzą jakąś spójną, logiczną całość (np. narzędzia do pracy z AD naszego autorstwa) – stwórzmy dla nich osobny moduł i importujmy go w profilu. Dzięki temu łatwiej nam będzie profilem zarządzać
  • jeśli korzystamy z ISE – użyjmy możliwości jakie ono daje i stwórzmy jedną ‘dziewiczą’ zakładkę, która da nam możliwość natychmiastowego rozpoczęcia pracy (moją metodę opisałem na moim blogu po angielsku, z całą pewnością wrócę do tego w kolejnych odcinkach tego cyklu.

Naprawdę, higiena profilu jest bardzo wskazana. I rzecz najważniejsza moim zdaniem: skorzystajmy z możliwości jakie daje nam PowerShell i podzielmy nasze dodatki na poszczególne ‘szczeble’ profilowego drzewka. I tak:

  • rzeczy używane wszędzie przez wszystkich (na ogół będą to aliasy, rzadziej pewnie funkcje, lub zmienne) –> AllUsersAllHosts
  • ustwienie zmiennych środowiska dla wszystkich w ramach danej konsoli/ hosta (czyli użycie funkcjonalności tegoż, jak stałe menu w ISE czy transkrypt w powershell.exe) –> AllUsersCurrentHost
  • nasze własne ustawienia, które przydadzą nam się niezależnie od używanej aktualnie konsoli (np. funkcje, niektóre moduły, aliasy) –> CurrentUserAllHosts
  • ustawienia własne pod konkretnego hosta (nasze moduły rozszerzające funkcjonalność ISE, czy używany przez nas moduł do PowerShell.exe, wszelkie kolorki, szmery i bajery, które nikomu prócz nas raczej się nie przydadzą) –> CurrentUserCurrentHost

Co to daje? Jeden punkt zarządzania. Naprawdę, to działa. W moim profilu mam np. funkcję:

function Get-WmiHelp {            
[CmdletBinding()]            
param (            
    [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory=$true)]            
    [Alias('Name')]            
    [wmiclass]$Class            
)            
process {            
    $Class.psbase.Options.UseAmendedQualifiers = $true            
    $Class.psbase.methods | Foreach-Object {             
        New-Object PsObject -Property @{            
            Class = $Class.Name            
            Name = $_.Name            
            Help = ($_.Qualifiers["Description"]).Value            
        }            
    }            
}            
<#
    .SYNOPSIS
        Displays info about methods for a given class.
    .DESCRIPTION
        This function is helper function.
        It gets information about methods in a given class.
        It uses [wmiclass] object to get information.
    .PARAMETER Class
        WMI Class to check for methods & method's help
    .EXAMPLE
        Get-WmiObject -List Win32_Network* | Get-WmiHelp
        Displays all methods for Win32_Network* classes with help.
        
    .EXAMPLE
        Get-WmiHelp -Class Win32_Process
        Display methods and help for Win32_Process
 
#>            
                
}            

Ponieważ działa równie dobrze w każdym z hostów – przechowuję ją w profilu *AllHosts. Dzięki temu jeśli kiedyś zechcę ją zmienić (np. dodać jakiś parametr) będę mógł to zrobić w jednym pliku. I niezależnie od użytego hosta – efekt uzyskam identyczny. To wygodne, dlatego odradzam definiowanie elementów, których funkcjonalność nie jest ściśle związana z bieżącym hostem w $profile. Nigdy nie wiadomo, kiedy będziecie zmuszeni przeskoczyć na innego hosta. 😉 A jaka funkcja kwalifikowałaby się do $profile? Np. ta ma rację bytu tylko w ISE:

            
function Edit-Function {            
            
    [CmdletBinding()]            
    param (            
    [Parameter(Mandatory=$true,             
        HelpMessage='Function name is mandatory parameter.')]            
    [ValidateScript({Get-Command -CommandType function $_})]            
    [string]            
    $Name            
    )            
              
    $file = $psISE.CurrentPowerShellTab.Files.Add()            
    $file.Editor.InsertText("function $name {`n")            
    $file.Editor.InsertText($(Get-Command -CommandType function $name |             
        Select-Object -ExpandProperty definition))            
    $file.Editor.InsertText("}")            
}

Pozwala ona otworzyć dowolną funkcję w części ‘skryptowej’ w sposób, który umożliwia jej prostą modyfikację ad-hoc. Naturalnie, w powershell.exe zaśmiecałaby tylko pamięć. Ale ponieważ takich funkcji (przygotowanych pod ISE) miałem nieco więcej – więc ostatecznie trafiły one do mojego modułu ISEFun, który staram się ciągle rozszerzać i udoskonalać. I zgodnie z tym co pisałem na początku – ładuję w $profile cały moduł. Profil mamy więc gotowy. I tym akcentem zamykam część drugą cyklu. Dalej zajmę się pewnymi trikami, które mogą uczynić profil jeszcze przyjaźniejszym. Skupię się na dwóch domyślnych hostach z tej prostej przyczyny, że z nich najczęściej korzystam. Nie omieszkam też wspomnieć o PowerGUI – tam modyfikacja profilu też ma rację bytu. Ale o tym wkrótce.