scenariusz
rozjechały się skrzynki na exchange. trzeba posprawdzać bazy oraz zreperować, co się da, z podglądem tego, co się dzieje w danym momencie.
podstawa
do weryfikacji i naprawy mailboxów online służy New-MailboxRepairRequest. problem polega na tym, że to polecenie nie generuje logu tylko wrzuca informacje do eventvwr. ta zmiana jest określana miałem 'alleluja, teraz jest zajebiście’ ale przeglądanie eventvwr, zwłaszcza na żywym systemie nie jest przyjemna – ani nie wiadomo kiedy co się kończy, co zaczyna, zdarzeń jest od groma, żądanie zrobione z linii poleceń, a weryfikacja w taki sposób… no generalnie słabe.
cel
byłoby fajnie móc mieć podgląd life na konsoli tego, jak przebiegają testy. zwłaszcza, jeśli chcemy co jakiś czas zapuścić weryfikację kolejnej 'podejrzanej’ skrzynki.
rozwiązanie
znalazłem w netcie kilka lamerskich skryptów, do podglądu eventvwr na żywo (-> keyword 'tail eventvwr’). czemu lamerskich? ponieważ działają na zasadzie nieskończonej pętli while($true) co już samo w sobie jest bolesne. w tej pętli co 1 sek pobierane jest ostatnie zdarzenie z logu aplikacji. czyli jeśli system jest na prawdę dociążony (a exchange potrafi taki być), może się okazać, że nie zobaczymy tego, na co czekamy. krótko mówiąc – DA SIĘ LEPIEJ (:
rozwiązaniem jest zastawienie pułapki – to nie my pytamy się logu o ostatni event, tylko niech log nam powie, że takowy się pojawił. kwestie wydajności pomijam bo nie badałem – na pewno trzeba pamiętać o wyrejestrowaniu pułapki, żeby nie dociążać systemu. mechanizmem, który to realizuje jest ’register-ObjectEvent’, który rejestruje pułapkę na przechwycenie zdarzenia dla obiektów .NET.
oczywiście nie każde polecenie zdarzenia generuje /: aby to sprawdzić, wystarczy zwykły get-member. sprawdźmy dla get-eventlog:
[PS] C:\Windows\system32>Get-EventLog -LogName application -Newest 1 |gm
TypeName: System.Diagnostics.EventLogEntry#application/MSExchange ADAccess/1074006048
Name MemberType Definition
---- ---------- ----------
Disposed Event System.EventHandler Disposed(System.Object, System.EventArgs)
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedTyp
Dispose Method System.Void Dispose()
Equals Method bool Equals(System.Diagnostics.EventLogEntry otherEntry), boo
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
ToString Method string ToString()
Category Property System.String Category {get;}
CategoryNumber Property System.Int16 CategoryNumber {get;}
Container Property System.ComponentModel.IContainer Container {get;}
Data Property System.Byte[] Data {get;}
EntryType Property System.Diagnostics.EventLogEntryType EntryType {get;}
Index Property System.Int32 Index {get;}
InstanceId Property System.Int64 InstanceId {get;}
MachineName Property System.String MachineName {get;}
Message Property System.String Message {get;}
ReplacementStrings Property System.String[] ReplacementStrings {get;}
Site Property System.ComponentModel.ISite Site {get;set;}
Source Property System.String Source {get;}
TimeGenerated Property System.DateTime TimeGenerated {get;}
TimeWritten Property System.DateTime TimeWritten {get;}
UserName Property System.String UserName {get;}
EventID ScriptProperty System.Object EventID {get=$this.get_EventID() -band 0xFFFF;}
no i słabo – niby jest 'disposed’ ale to nic nie daje. a to dla tego, że obiektem, który jest zwrócony, jest konkretny 'event’. jest jednak wywołanie polecenia, które 'podłącza się’ do eventlogu a nie do jego zawartości:
[PS] C:\Windows\system32>get-eventlog -List|gm
TypeName: System.Diagnostics.EventLog
Name MemberType Definition
---- ---------- ----------
Disposed Event System.EventHandler Disposed(System.Object, System.EventArgs)
EntryWritten Event System.Diagnostics.EntryWrittenEventHandler EntryWritten(System.Object, System....
BeginInit Method System.Void BeginInit()
Clear Method System.Void Clear()
Close Method System.Void Close()
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Dispose Method System.Void Dispose()
EndInit Method System.Void EndInit()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
ModifyOverflowPolicy Method System.Void ModifyOverflowPolicy(System.Diagnostics.OverflowAction action, int ...
RegisterDisplayName Method System.Void RegisterDisplayName(string resourceFile, long resourceId)
ToString Method string ToString()
WriteEntry Method System.Void WriteEntry(string message), System.Void WriteEntry(string message, ...
WriteEvent Method System.Void WriteEvent(System.Diagnostics.EventInstance instance, Params System...
Container Property System.ComponentModel.IContainer Container {get;}
EnableRaisingEvents Property System.Boolean EnableRaisingEvents {get;set;}
Entries Property System.Diagnostics.EventLogEntryCollection Entries {get;}
Log Property System.String Log {get;set;}
LogDisplayName Property System.String LogDisplayName {get;}
MachineName Property System.String MachineName {get;set;}
MaximumKilobytes Property System.Int64 MaximumKilobytes {get;set;}
MinimumRetentionDays Property System.Int32 MinimumRetentionDays {get;}
OverflowAction Property System.Diagnostics.OverflowAction OverflowAction {get;}
Site Property System.ComponentModel.ISite Site {get;set;}
Source Property System.String Source {get;set;}
SynchronizingObject Property System.ComponentModel.ISynchronizeInvoke SynchronizingObject {get;set;}
bingo! tu pojawia się 'entryWritten’! a więc zarejestrujmy pułapkę, przefiltrujmy zdarzenia, które nas interesują – i jeśli takowe się pojawi, wyświetlmy na ekran. opis eventów, jakie generuje new-mailboxrepairrequset można znaleźć na technecie. poniżej draft skryptu, który można sobie dowolnie rozbudować. dla czytelności zapisany w kilq linijkach.
$evtlog=Get-EventLog -list|?{$_.log -eq 'application'}
Register-ObjectEvent -InputObject $evtlog -EventName EntryWritten -SourceIdentifier EventTrap -Action{
$id=$event.SourceEventArgs.Entry.EventID;
if($id -gt 10041 -and $id -lt 10062) {
write-host "$id -> $($event.SourceEventArgs.Entry.Message)"
}
}
na początq podłączamy się do konkretnego logu – logu aplikacji. następnie dla tego logu rejestrujemy pułapkę na pojawienie się nowych zdarzeń i definiujemy akcję. ponieważ na poziomie rejestracji nie ma możliwości zdefiniowania żadnych filtrów, wszystko trzeba zdefiniować wewnątrz ciała skryptu. po dokonaniu rejestracji, w konsoli będą się pokazywać wszystkie logi pomiędzy 10041-10062. oczywiście warto je ładniej wyświetlić i zrobić pełną obsługę… aby tego dokonać, kluczowym jest obiekt $event.SourceEventArgs.Entry, który jest de facto eventem pojawiającym się w logu, czyli można dostać się do wszystkich parametrów takich jak przy ’ Get-EventLog -LogName application -Newest 1 |gm’.
na koniec trzeba pamiętać o wyrejestrowaniu!! na skróty, wyrejestrowanie wszystkich:
get-event|unregister-event
eN.
Exchange – uprawnienia do kalendarzy zasobów
ogólnie
prosty scenariusz: trzeba założyć zasób, reprezentujący samochód firmowy tak, aby ludzie mogli go sobie rezerwować. następnie chcemy aby wszyscy widzieli kto go wypożyczył i dokąd zabrał. standardowo większość informacji jest ukryta i widać tylko ogólny wpis.
w tym celu należy nadać uprawnienia do kalendarza nie jest trudno i jest sporo linków, które szybko wskażą, że można to zrobić poleceniem:
… ale:
lokalizacja
czemu w PL nie zadziała? ponieważ 'calendar’ to nazwa katalogu, a ta jest zlokalizowana. żeby było śmieszniej nazwa będzie zależna od… ustawień przeglądarki w momencie tworzenia obiektu zasobu (użytkownikom definiuje się kraj). sweet. ponieważ mam główną przeglądarkę ustawiona na en-US, żeby nie oglądać raniących w oczy auto-tłumaczeń technetowych czy portalu office365 w którym ciężko się połapać, część obiektów ma 'calendar’ a część 'kalendarz’. zakładając z command line zawsze tworzy 'calendar’ pomimo polskich regionalsów, więc mniemam, że ten sposób jest bardziej deterministyczny (ale trzeba by porobić więcej testów na różnie zlokalizowanych systemach).
czyli zapis ścieżki do konkretnego folderu to „roomResource@wfiles.pl:\kalendarz” . i znów – tyle dość łatwo i szybko można wyszperać na forach… ale pozostaje pytanie – jak wylistować wszystkie katalogi?? skoro jest kalendarz, to pewnie są jakieś inne foldery i może da się z nimi coś zrobić? i jak sprawdzić czy to jest 'calendar’ czy 'kalendarz’? ale najpierw…
uprawnienia
standardowo, po utworzeniu zasobu sali lub ekwipunq jedyne co widać w kalendarzu to 'busy’.
można to zmienić i umożliwić bardziej pełny widok nadając uprawnienia LimitedDetails lub Reviewer (pełna lista uprawnień tutaj). różnica jest dość poważna. ograniczone detale pozwolą na zobaczenie organizatora i lokalizacji ale nie pozwolą otworzyć zdarzenia ze względu na brak uprawnień:
nadanie uprawnień podglądacza (; pozwoli otworzyć wpis w kalendarzu i w pełni mu się przyjrzeć:
a gdzie informacje??
na pierwszy rzut oka może wygląda dobrze, ale ktoś choć trochę bardziej spostrzegawczy zauważy, że pole tematu oraz szczegółowe informacje chyba nie dają pełnych informacji. i faktycznie – standardowo, założenie jest takie: ktoś wynajmuje salę, wysyła zaproszenie, więc w tym zaproszeniu umieszcza informacje o czym będzie rozmowa, pewnie agendę, może jakieś materiały dodatkowe itd. zasób sali dostaje to samo zaproszenie co każdy inny – a to byłby niekontrolowany wyciek informacji. nie byłoby miło, gdyby cała firma wiedziała, że właśnie dyrektorzy spotykają się, np. żeby omówić zwolnienia pracowników… dla tego standardowe opcje są ustawione restrykcyjnie i wycinają wszystkie informacje.
są jednak scenariusze, w których niektóre osoby muszą wiedzieć więcej – np. dział logistyki musi wiedzieć gdzie jest samochód i po co. wtedy trzeba wykonać następujące operacje:
zobaczmy jakie zachowania można dla zasobu ustawić:
wszystkich nie ma sensu tłumaczyć, można przeczytać na technecie, nas interesują konkretnie DeleteSubject, DeleteComents. jak widać można również pozwolić na załączniki, kontrolować czy zatwierdzanie jest automatyczne czy wymaga ręcznego potwierdzenia, czy w polu tematu dodawana jest nazwa użytkownika… i inne. przykładowa konfiguracja zasobu:
i w takim przypadq dział logistyki może zobaczyć takie krzaczki…
lista folderów
na koniec powrócę do listy folderów. tu niestety niespodzianka: nie da się wylistować folderów dla zasobu. ponieważ zasób jest skrzynką… ale równocześnie nią nie jest. jest – i te foldery są, ale nie jest – bo tak twierdzi system. poniżej mała zabawa – wylistowanie folderów dla regularnego mailboxa, próba wylistowania dla zasobu i dowód, że jednak wszystkie foldery są…
przy czym normalny user ma nazwy katalogów po polsq bo zakładając tak miał zdefiniowaną licencję, a zasób po ang. bo był zakładany z linii poleceń, a zasoby licencji nie posiadają. wykorzystanie 'get-mailboxfolderpermission’ w połączeniu z intuicją to jedyny sposób jaki znalazłem, żeby sprawdzić czy dany folder istnieje dla zasobu…
eN.