pátek 17. dubna 2009

Hej, mistře Šelíku!

Při pročítání posledního CZ TechNet zpravodaje mě napadlo zpracovat téma z komiksu Hej, mistře Skriptíku! v PowerShellu. TechNet Flash zpravodaj je dostupný na www.technetflash.cz (včetně starších čísel). Rozhodně jej doporučuji všem IT odborníkům.


Tentokrát budeme potřebovat PowerShell v2 (CTP3). Tato verze již obsahuje cmdlety pro práci s Windows Event Logy:

PS C:\Users\beta> Get-Command -noun EventLog | Select-Object Name
Clear-EventLog
Get-EventLog
Limit-EventLog
New-EventLog
Remove-EventLog
Show-EventLog
Write-EventLog

Mrkneme se na Write-EventLog. Ten má čtyři povinné parametry: LogName, Source, EventID, Message. Zkusme tedy něco zapsat do aplikačního logu:

Write-EventLog -LogName Application -source "Windows Error Reporting" -eventID 007 -Message "James Bond is cool!"

Výborně, stále tomu ale něco chybí. Většinou budete chtít zapisovat data z vašich programů. Pokud zkusite zápis ve tvaru:

Write-EventLog -LogName Application -source "Moje Aplikace" -eventID 007 -Message "James Bond is cool!"

PowerShell vám vrátí chybu jejíž část The source was not found vám oznámí, že toto nelze. Je to způsobené tím, že každý zdroj (Source), který zapisuje do Event Logu musí být předem zaregistrován (viz MSDN). Naštěstí exituje v PowerShellu cmdlet New-EventLog, který tuto službu udělá za vás. Následující seznam příkazů již tedy povede ke zdárnému konci (pro registraci zdroje musíte příkaz pustit s právy administrátora):

PS C:\> New-EventLog -LogName Application -Source "Moje Aplikace"
PS C:\> Write-EventLog -LogName Application -source "Moje Aplikace" -eventID 001 -Message "Ale ja jsem jednicka."

pátek 13. března 2009

smsDiagram - Draw your SMS hierarchy with one script

The Story
I am an administrator of big SMS hiearchy. Few times I reported to management how our infrastructure looks like. You know managers - they doesn't understand to objects, pipes and other funny parts of admin life. They are like children - likes pictures and graphs. So it's nice to show them infrastructure drawn in Visio. Till now I created it manually but after we merged few sites together it's not fun anymore (we have about 300 servers).

Maybe you recognized small analogy in the title of this post. When I saw an article written by Alan Renouf I knew that my future is bright. So thanks to Alan to kick me (Virtu-Al(ly) of course :)

The Script
Script itself is not so complicated. The biggest part is related to Visio (I use 2003 version) drawing. But there are some parts you might find useful for your own scripts.

Load hierachy from WMI
  1. $siteCode = (Get-WmiObject -ComputerName $server -Namespace root/sms `
  2. -Class SMS_ProviderLocation -Filter "ProviderForLocalSite='True'").SiteCode
  3. # SMS Namespace path
  4. $SMSWMINamespace = 'root/sms/site_'+$siteCode
  5. # List of children servers from SMS_Site class
  6. $servers = Get-WmiObject -ComputerName $server -Namespace $SMSWMINamespace -Class SMS_Site | `
  7. Select ReportingSiteCode, SiteCode, ServerName, Type | `
  8. Sort ReportingSiteCode, SiteCode
At lines 1-2 we have one-liner which connects to WMI of the site server (specified as parameter of the script) and find sitecode in SMS_ProviderLocation class. Then we connect to the SMS site namespace. We can load all necessary information from that namespace (lines 7-9) and save it in $servers object.

Text representation of SMS hierarchy
If you don't have Visio or just want quick overwiew you have two choices:
  1. Use standard SMS report (Sites by hierarchy with time of last site status update)
  2. Pass parameter $textInfo to smsDiagram script :)
Second option will display your hierarchy in this way:

PS C:\> ./smsDiagram.ps1 -server SMS01 -textInfo
CC0 (P)
  CZ0 (P)
    500
    HU0 (P)
      501
  ZZ0 (P)
    003
    004
    ZZ1 (P)

It's easy and fast - as fast as your connectivity to the site server. (P) means that the displayed server is Primary server.

Voila
And here is the nicest part of the script. It uses standard Visio Network template - Detailed Network Diagram. If you want to show infrastructure as Visio diagram just provide site server name as a parameter. In the diagram you can also see which server is Primary (db icon) and which is Secondary.

PS c:\> smsDiagram.ps1 -server SMS01



Now I have latest SMS hierarchy at my desk and it looks awesome :) Final document is saved in your My Documents folder, name is smsDiagram.vsd.

Notes
I didn't test the script against ConfigMgr. My "test lab" is not active now (to be honest - my NB where I ran it crashed before few days and I haven't time to rebuild it now).
No error codes checking now. Be sure you run the script as admin able to connect to WMI of site server.
Sometimes the Visio diagram is "ugly" - it fully depends on how the Visio handle objects creation. Hopefuly will find some solution but it's not a priority now.
Default text info shows site code. For next version I plan to add switch which tells what do you want to show as server description.
For the future versions of the script I also would like to add some more features - display server roles (MP, CAP, ...), customize output (shape names, connectors).
Source code is available at PoSh Code.

Please let me know if you find it useful or if you find some errors. Any feedback for improvement is welcome.

pondělí 9. března 2009

Looking for HResult

In one of the last Hey, Scripting Guy! article is described how to install updates and check it's result codes. I was really surprised when Scripting guys told me: "We can use calc.exe" - OMG why not use converting directly in PowerShell?

PS C:\> "0x{0:x}" -f [int] -2145124318
0x80240022

When I tried this I start thinking about the function which will do the conversion and shows also description of the result code.

  1. # Name : Get-HResult.ps1
  2. # Author: David "Makovec" Moravec
  3. # Web : http://www.powershell.cz
  4. # Email : powershell.cz@googlemail.com
  5. #
  6. # Description: Finds meaning of HResult
  7. #
  8. # Version: 0.1
  9. # History:
  10. # v0.1 - (add) basic functionality
  11. #
  12. # Usage: Get-HResult -2145124318
  13. #
  14. #################################################################
  15. function Get-HResult {
  16. param (
  17. $HResult,
  18. $url = 'http://technet.microsoft.com/en-us/library/cc720442.aspx'
  19. )
  20. $calc = "0x{0:x}" -f [int]$HResult
  21. $lookFor = $calc+'.*?tr'
  22. $wc = New-Object system.net.webclient
  23. $wc.DownloadString($url) -match $lookFor | Out-Null
  24. $tmp = $matches[0]
  25. $searchString = '(?<ResCode>'+$calc+').*\<p\>(?<ResStr>.+?)\<.*\<p\>(?<Description>.+?)\</p.+'
  26. $tmp -match $searchString | Out-Null
  27. Write-Host "HResult: " $HResult
  28. Write-Host "Result Code: " $matches.ResCode
  29. Write-Host "Result String: " $matches.ResStr
  30. Write-Host "Description: " $matches.Description
  31. } #function

By default script return results for Windows Update Agent and has no checking for errors. It has parameter for providing different URL but I didn't try any. Usage is following:

PS C:\> Get-HResult -2145124351
Result Code: 0x80240001
Result String: WU_E_NO_SERVICE
Description: Windows Update Agent was unable to provide the service.

Source code will be available at PoSh Code when I'll be able to open it, now have troubles with connectivity.

pátek 6. března 2009

Hlasování

Trošku jsem přemýšlel nad obsahem tohoto webu. Vzhledem k tomu, že některé z příspěvků bych rád publikoval "mezinárodně", překládám částečně již existující příspěvky.
Proto bych se rád zeptal, jestli by vám vadilo, kdyby byl obsah webu psán v anglickém jazyce. Děkuji za vaše hlasy.

čtvrtek 26. února 2009

Quest Connect

[Update]: Dmitry Sotnikov potvrdil, že se bude jednat o on-line diskusi na téma PowerShell a PowerGUI.

Podle mailu, který mi dnes přistál v poště, to vypadá, že se blíží další konference Quest Connect. Je naplánována na 11. března 2009.
Zatím to vypadá, že část bude živě a část bude archiv minulé konference. Až budu vědět více, dám vědět.

pátek 23. ledna 2009

Jak najít význam zkratky

Poslední dobou hodně komunikuji s lidmi, kteří tráví [četováním] evidentně hodně času i mimo práci. Používají množství zkratek, ze kterých jsem občas trochu mimo. Jelikož pak většinou nechápu odpovědi typu BFN, ACAB a podobně. Do nynějška jsem vždy použil Google a ne vždy se trefil napoprvé. Rozhodl jsem se vytvořit funkci, která vyhledá význam zkratky na internetu. Po vyzkoušení několika stránek se mi zalíbila (jak z hlediska vrácených odpovědí, tak i z hlediska struktury HTML stránky) stránka Acronyms and Abbreviations na TheFreeDictionary.

Vrácené HTML je ve tvaru, který je vidět na obrázku



Pomocí XPath jsem se dotázal na všechny druhé sloupce a výsledkem je seznam významů. Pro stažení a parsování výsledku hledání jsem použil Invoke-Http a Receive-Http z HttpRest. Použití je jednoduché:


PS C:\> Find-Abbreviation itmu
Inventory Tool for Microsoft Updates

  1. # Name : Find-Abbreviation.ps1
  2. # Author: David "Makovec" Moravec
  3. # Web : http://www.powershell.cz
  4. # Email : powershell.cz@googlemail.com
  5. #
  6. # Description: Finds meaning of given abbreviation
  7. # : Uses HttpRest http://poshcode.org/787
  8. #
  9. # Version: 0.1
  10. # History:
  11. # v0.1 - (add) basic functionality
  12. #
  13. # Usage: Find-Abbreviation fyi
  14. #
  15. #################################################################
  16. function Find-Abbreviation {
  17. $url = "http://acronyms.thefreedictionary.com/$args"
  18. Invoke-Http get $url | Receive-Http Text "//tr[@cat]//td[2]"
  19. }
Skript je ke stažení na PoshCode.

pátek 2. ledna 2009

Operátor formátování - 3. část

V minulém díle jsme skončili ukázkou různých formátů data. Dnes si ukážeme, jak si můžeme vytvořit formát vlastní, pokud nám předdefinované nestačí. Vraťme se k mému oblíbenému ShortDatePattern. Uložíme si aktuální datum a čas do proměnné.

PS C:\> $d = Get-Date
PS C:\> $d

31. prosince 2008 22:32:33

PS C:\> "{0:d}" -f $d
31.12.2008
PS C:\> (Get-Culture).DateTimeFormat

ShortDatePattern : d.M.yyyy

0:d (standardní formát) reprezentuje zobrazení ve tvaru d.M.yyyy (uživatelský formát). V uživatelském formátu je význam znaků následující:
  • d – den ve tvaru 31
  • M – měsíc ve tvaru 12
  • yyyy – rok ve tvaru 2008
  • . – funguje jako oddělovač jednotlivých položek
Když si vyzkoušíte následující dva příklady

PS C:\> "{0:d}" -f $d
31.12.2008
PS C:\> "{0:d.M.yyyy}" -f $d
31.12.2008

dostanete naprosto shodný výsledek. V tomto případě je tedy jednodušší použít standardní formát a ušetřit sedm znaků, než si formát data vytvořit po svém. Pokud ovšem budete chtít například jiný oddělovač dne a měsíce, je dobré znát uživatelské formátování a potřebný výstup si vytvořit.

PS C:\> "{0:~d~ [M] (yyyy)}" -f $d
~31~ [12] (2008)

Jak PowerShell (.NET) pozná, zda d, které jsme zadali, je ShortDatePattern nebo uživatelské zobrazení dne? Existuje jednoduché pravidlo – pokud formátovací řetězec obsahuje více než jeden znak (počítá se i mezera!), je brán jako uživatelský formát, čili

PS C:\> "{0:d}" -f $d
31.12.2008
PS C:\> "{0:d }" -f $d
31

V následující části se podíváme na všechny formátovací řetězce. Pro snažší zapamatování jsem se snažil sdružit je do kategorií.

Den
  • d – zobrazuje den jako číslo ve tvaru 1..31
  • dd – zobrazuje den jako číslo ve tvaru 01..31 (přidává nulu v případě, že pořadové číslo dne je menší než 10).
  • ddd – zobrazuje zkratku dne. Jak je definována můžete zjistit pomocí příkazu (Get-Culture).DateTimeFormat.AbbreviatedDayNames
  • dddd – zobrazuje jméno dne, viz (Get-Culture).DateTimeFormat.DayNames
Ukažme si rovnou nějaké příklady

PS C:\> "{0:d}" -f $d
31.12.2008
PS C:\> "{0:d }" -f $d
31
PS C:\> "{0}" -f $d.day
31
PS C:\> "{0:dd}" -f $d
31
PS C:\> "{0:ddd}" -f $d
st
PS C:\> "{0:dddd}" -f $d
středa
PS C:\> (Get-Culture).DateTimeFormat | fl *DayNames

AbbreviatedDayNames : {ne, po, út, st...}
ShortestDayNames : {ne, po, út, st...}
DayNames : {neděle, pondělí, úterý, středa...}

Měsíc
  • M – měsíc jako číslo ve tvaru 1..12
  • MM – měsíc jako číslo ve tvaru 01..12
  • MMM – zkratka měsíce dle (Get-Culture).DateTimeFormat.AbbreviatedMonthNames – zobrazení je ve tvaru římských číslic
  • MMMM – jméno měsíce v plném tvaru, viz (Get-Culture).DateTimeFormat.MonthNames
PS C:\> "{0:M}" -f $d
31 prosince
PS C:\> "{0:M }" -f $d
12
PS C:\> "{0}" -f $d.Month
12
PS C:\> "{0:MM}" -f $d
12
PS C:\> "{0:MMM}" -f $d
XII
PS C:\> "{0:MMMM}" -f $d
prosinec
PS C:\> (Get-Culture).DateTimeFormat | fl *MonthNames

AbbreviatedMonthNames : {I, II, III, IV...}
MonthNames : {leden, únor, březen, duben...}

Nyní jsem již podruhé použil konstrukci, která se vám možná moc nelíbí. Jde o `0:M ` (s mezerou za znakem M). Z ukázek to není patrné, ale zvídavější povahy dospěly ke (správnému) názoru, že mezera navíc se nám projeví ve výstupním řetězci a občas nám to může vadit. Pojďme si to ověřit.

PS C:\> "{0:M }" -f $d
12
PS C:\> ("{0:M }" -f $d).length
3
PS C:\> Write-Host "|$($a[2])|"
| |

Mezera jak vyšitá :) Jak se tohoto jevu zbavit si ukážeme na konci dnešního článku. Do té doby budeme všechny jednopísmenné formátovací řetězce zapisovat s mezerou za znakem.

Ještě jedna malá odbočka před tím, než budeme pokračovat. V následujících příkladech budu v některých částech od aktuálního data odčítat určitý počet dní, hodin, … Pro Ty, kteří zatím moc nepracovali s třídou DateTime či příkazem Get-Date uvádím malou ukázku (za znaky ### bude vždy následovat komentář k příkazu).

PS C:\> $d | Get-Member Add* ### Zobrazíme všechny metody pracující s přičítáním času


TypeName: System.DateTime

Name MemberType Definition
---- ---------- ----------
Add Method System.DateTime Add(TimeSpan value)
AddDays Method System.DateTime AddDays(Double value)
AddHours Method System.DateTime AddHours(Double value)
AddMilliseconds Method System.DateTime AddMilliseconds(Double value)
AddMinutes Method System.DateTime AddMinutes(Double value)
AddMonths Method System.DateTime AddMonths(Int32 months)
AddSeconds Method System.DateTime AddSeconds(Double value)
AddTicks Method System.DateTime AddTicks(Int64 value)
AddYears Method System.DateTime AddYears(Int32 value)

PS C:\> "{0}" -f $d ### Vypíšeme aktuální stav proměnné d
31.12.2008 22:32:33
PS C:\> "{0}" -f $d.AddDays(1) ### Přičteme jeden den
1.1.2009 22:32:33
PS C:\> "{0}" -f $d.AddHours(2) ### Přičteme dvě hodiny
1.1.2009 0:32:33
PS C:\> ### V následujících příkazech budeme časové jednotky odčítat
PS C:\> "{0}" -f $d.AddDays(-366) ### Odečteme 366 dní (rok 2008 byl přestupný)
31.12.2007 22:32:33
PS C:\> "{0}" -f $d.AddSeconds(-611) ### A co třeba nějaký zapamatovatelný čas
31.12.2008 22:22:22

Důležité je z předchozích příkazů pochopit, že v čase můžeme jít i do minulosti.

Rok
  • y – rok jako max. jedno nebo dvouciferné číslo.
  • yy – rok jako dvouciferné číslo.
  • yyy – zobrazí rok jako minimálně trojciferné číslo. Pokud má rok více než tři významové číslice jsou i ty zobrazeny ve výsledku.
  • yyyy – zobrazí rok jako čtyřciferné číslo. Pokud má rok méně než čtyři čísla je zleva doplněn nulami.
  • yyyyy – zobrazí rok jako pěticiferné číslo. Pokud má rok méně než pět číslel je zleva doplněn nulami.
PS C:\> "{0:y}" -f $d
prosinec 2008
PS C:\> "{0:y }" -f $d
8
PS C:\> "{0:yy}" -f $d
08
PS C:\> "{0:yyy}" -f $d
2008
PS C:\> "{0:yyyy}" -f $d
2008
PS C:\> "{0:yyyyy}" -f $d
02008
PS C:\> "{0:yyy}" -f $d.AddYears(-2000)
008
PS C:\> "{0:yyyyy}" -f $d.AddYears(-2000)
00008

Hodina
  • h – zobrazuje hodinu jako číslo z intervalu 1..12. Jedná se tedy o zobrazení dvanáctihodinového cyklu.
  • hh – zobrazuje hodinu jako číslo z intervalu 01..12.
  • H – zobrazuje hodinu jako číslo z intervalu 0..23. Zobrazuje dvacetičtyřhodinový formát.
  • HH – zobrazuje hodinu jako číslo z intervalu 00..23.
PS C:\> "{0:h}" -f $d
Exception retrieving string: "Input string was not in a correct format."
At line:1 char:11
+ "{0:h}" -f <<<< $d
PS C:\> "{0:h }" -f $d.AddHours(-1)
9
PS C:\> "{0:hh}" -f $d.AddHours(-1)
09
PS C:\> "{0:H }" -f $d.AddHours(-1)
21
PS C:\> "{0:HH}" -f $d.AddHours(-1)
21

V případě znaku h byla vyhozena výjimka z toho důvodu, že pro standardní formátování data neexistuje formátovací řetězec h. Tu samou chybu obdržíte, pokud zkusíte např. znak x.

Minuta
  • m – minuta ve tvaru 0..59
  • mm – minuta ve tvaru 00..59
PS C:\> "{0:m}" -f $d
31 prosince
PS C:\> "{0:m }" -f $d
32
PS C:\> "{0:m }" -f $d.AddMinutes(-30)
2
PS C:\> "{0:mm}" -f $d.AddMinutes(-30)
02
PS C:\> "{0:mm}" -f $d
32

Sekunda
  • s – sekunda ve tvaru 0..59
  • ss – sekunda ve tvaru 00..59
Celá idea už je nyní asi jasná a proto uvedeme pouze jeden příklad.

PS C:\> "{0:ss}" -f $d
33

Při zobrazování času můžeme jít až na úroveň milisekund. Formátovacím řetězcem je znak f (a F). V současné době máme v proměnné d uložen čas, který má v sobě hodnotu 290ms. Pro názornost budeme tedy v následujících příkladech odečítat 200 ms.

PS C:\> "{0:H:mm:s:f}" -f $d.AddMilliseconds(-200)
22:32:33:0
PS C:\> "{0:H:mm:s:F}" -f $d.AddMilliseconds(-200)
22:32:33:
PS C:\> "{0:H:mm:s:ff}" -f $d.AddMilliseconds(-200)
22:32:33:09
PS C:\> "{0:H:mm:s:FF}" -f $d.AddMilliseconds(-200)
22:32:33:09
PS C:\> "{0:H:mm:s:fff}" -f $d.AddMilliseconds(-200)
22:32:33:090
PS C:\> "{0:H:mm:s:FFF}" -f $d.AddMilliseconds(-200)
22:32:33:09

V zobrazení milisekund je tedy vidět malá změna při zobrazování – zobrazujeme vždy hodnoty „zleva“, což ale dává smysl, protože potřebujeme znát významově nejvyšší číslici. V zobrazování můžeme jít až na úroveň sedmi f (fffffff, FFFFFFF), ale z hlediska přesnosti to nemá význam (dle MSDN je přesnost hodin většiny systémů Windows 10-15ms).

„Speciální“ formátovací řetězce
.NET zavádí ještě několik dalších znaků, které můžeme při zobrazení data a času použít.
  • g – zobrazuje éru, čili n. l. pro současný rok
  • K, z – zobrazuje informace o časové zóně
  • t, tt – označení dopoledne/odpoledne dle System.Globalization.DateTimeFormatInfo.AMDesignator a System.Globalization.DateTimeFormatInfo.PMDesignator
  • : / - zastupují separátory času a datumu definované dle System.Globalization.DateTimeFormatInfo.TimeSeparator a System.Globalization.DateTimeFormatInfo.DateSeparator
  • “ ‘ – zobrazí text uvedený v uvozovkách
  • % - zobrazí výsledek formátovacího řetězce braného jako uživatelský formát. Jinými slovy od této doby můžete použít místo znaku mezery za formátovacím řetězcem použít procento před, např. %d.
  • \ - zobrazí znak zapsaný za lomítkem, např. \h
  • jakýkoli jiný znak způsobí vyhození výjimky.
PS C:\> (Get-Culture).DateTimeFormat

AMDesignator : dop.
PMDesignator : odp.
DateSeparator : .
TimeSeparator : :

PS C:\> "{0:d}" -f $d
31.12.2008
PS C:\> "{0:d }" -f $d
31
PS C:\> "{0:%d}" -f $d
31
PS C:\> ("{0:%d}" -f $d).length
2

PS C:\> "{0:%g}" -f $d
n. l.

PS C:\> "{0:d \d 'd'}" -f $d
31 d d

Po předcházejcích ukázkách už zřejmě chápete, proč pomocí “{0:d}” –f $d dostaneme výsledek 31.12.2008. Takže se pojďme podívat na trochu složitější příklad. Ve druhém díle jsme si ukazovali i univerzální setříditelný formát, který se zapisuje pomocí formátovacího řetězce u. Je tvořen následujícím způsobem:

PS C:\> "{0:u}" -f $d
2008-12-31 22:32:33Z
PS C:\> [System.Globalization.DateTimeFormatInfo]::CurrentInfo.UniversalSortableDateTimePattern
yyyy'-'MM'-'dd HH':'mm':'ss'Z'

Zleva doprava jsou postupně zobrazeny tyto údaje: rok ve čtyřčíselném formátu (2008), pomlčka (-), měsíc ve dvouciferném formátu (12), pomlčka (-), den ve dvouciferném formátu (31), mezera, hodiny ve čtyřiadvacetihodinovém dvouciferném formátu (22), minuty dvouciferně (32), sekundy dvouciferně (33), písmeno Z.

PS C:\> "Den: {0:%d}`nMesic: {1:%M}`nRok: {2:yyyy}`nJe tedy {0:%d}. {3} {2:yy}" -f $d, $d, $d, `
>> (Get-Culture).DateTimeFormat.MonthGenitiveNames[$d.Month-1]
>>
Den: 31
Mesic: 12
Rok: 2008
Je tedy 31. prosince 08

V předchozím příkladu jsem použil jednu dosud nezmíněnou vlastnost třídy DateTimeMonthGenitiveNames, výsledek je vidět ve výstupu.

Po pročtení toho dílu byste měli být schopni formátovat datum v jakémkoli tvaru. Jako vždy si můžete stáhnout zdrojové kódy. Nicméně i tak doporučuju si všechny příklady (i vaše vlastní vymyšlené) pěkně natlouct přes klávesnici. Jen tak vám přejdou do krve.