Egyik ügyfelünket az a szerencse érte, hogy egyesítenie kellett saját internetes/belső levelezését anyacége belsős levelezésével. Beszélhetnék a két Exchange organizáció egyesítésének szépségeiről, de most elég lesz, ha csak a címekkel foglalkozom.
Nem kis méretekről van szó: az egyesítés révén több, mint harmincezer új cím fog megjelenni a címlistájukban a jelenlegi ezer mellett – miközben az új címeket csak a top50 felhasználó használja.
Megjósolható, hogy itt nagyokat kell majd varázsolni a címlistákkal.

    Kitérő:
    Ha valaki úgy képzeli el a címlistát, hogy van egy nagy lajstrom, benne a tagok neve – az hatalmasat téved. Szó sincs ilyesmiről. Az egész lista tulajdonképpen egy bazi nagy LDAP lekérdezés. (Érdemes megnézni az Exchange System Managerben a címlista tulajdonságlapján. Igen, arról a kínai szövegről van szó. Később részletezni is fogom.) Lehet ujjongani: mekkora ötlet, nincs lista, nincs egyenként postafiókba firkálgatás, egyszerűen van időnként egy gyors ldap query és ennek eredményét használják a kliensek. És az ldap query gyors – hiszen ez az értelme az egész adatbázis faszerkezetnek. (Már látom előre, hogy erre a szóra mennyire rá fognak harapni a keresőrobotok.:)
    Nos, ha kiujjongtuk magunkat, jöhet a keserves ébredés. Indítsunk el egy ldp-t (support tools) és vizsgáljuk meg egy felhasználó értékkel is bíró tulajdonságait. Fókuszáljunk rá a showInAddressBook tulajdonságra és lassan forduljunk le a székről. Igen. Minden exchange tulajdonságokkal rendelkező objektum tulajdonságai közé be van vésve, hogy mely címlistáknak a tagja.
    Láthatod, nem hazudtam. Nevekből álló lista nincs. Van lekérdezés és van objektumba bevésés. Mindezzel sikerült ötvözni a kényelmetlenséget a lassúsággal. Szerencsére a lassú folyamatok időzíthetők, így végülis nem vészes. (De erről megint később.)

No, nézzük a feladatot. A fentről jövő címeket egy OU szerkezetbe fogja beletojni a MIIS, kontaktok formájában. Természetesen a globális címlistában (Global Address List, GAL) meg fognak jelenni, hiszen azért globális a szerencsétlen.

Ötlet1.
Csináljunk egy alternatív globális címlistát. Na jó, értem én, nem kell kötekedni – igen, ez már nem globális. Csináljunk egy listát és valahogy szűrjük ki a beszülető címeket. Mivel az ügyfélnek is vannak kontaktjai, a legegyszerűbb szűrés – kihajítjuk a kontaktokat – kizárható. Vegyük elő megint az ldp-t, nézzük végig az egyes objektumok tulajdonságait, hátha találunk valami jó szűrőfeltételt. Nos, nem. Nincs. Ez nagyon rossz hír. Kénytelenek leszünk csinálni egyet. Erre valók az ún custom attribute tulajdonságok, van is belőlük tizenhat. Most még csak tesztelünk, tehát egy-egy kontakt, csoport illetve felhasználói postafiók második custom attribute tulajdonságába beírjuk, hogy Valami. Innentől már csak csinálni kell egy teszt címlistát, majd összekattogtatjuk a szűrést ezen tulajdonság értékére. Ja, igen, elfelejtettem mondani, az Address List tulajdonságlapján az egyik fül alatt egy kattogtatógép lapul, ezzel lehet összeállítani ldap query-ket. Legalábbis ez volt a szándék. Sajnos a megvalósítás ettől igen messzire került. Röviden fogalmazva botrányos: a fenti feltételt spec nem lehet összeállítani.
Erről van szó: gyűjts le mindenkit, akinél
vagy userpostafiok.customattribute2=’Valami’
vagy kontakt.customattribute2=’Valami’
vagy group.customattribute2=’Valami’.
Nem egy nagy ügy, így nézne ki:
(|
(&(objektum=user)(customattribute2=Valami))
(&(objektum=kontakt)(customattribute2=Valami))
(&(objektum=group)(customattribute2=Valami))
)
A fenti példán remekül el lehet magyarázni az ldap lekérdezés szintaktikáját. Kezdjük az ún. lengyel logikával. Nem azt mondják, hogy ‘3+3=’, hanem azt, hogy ‘+(3)(3)=’. Az & jelenti az ‘és’ műveletet, a | a ‘vagy’ műveletet és a ! a tagadást.
A valóság ennél jóvabb cifrább, nyilván meg kell vizsgálni, hogy egyáltalán léteznek-e a tulajdonságok és az objektum beazonosítása sem ilyen egyszerű.
Kezdjük a kattogtatást.
Első fül: Kellenek a kontaktok, a postafiókok és a csoportok.
Második fül: Kell az összes Exchange szerver.
Marha jól haladunk, már csak egy fül van hátra, ahol a customattribute2 értékét kellene megadnunk.
Harmadik fül: Izé. Csak úgy nem lehet megadni, előtte ki kell választani, hogy user vagy kontakt vagy group. Válasszuk először a felhasználót. Ekkor a tesztnél megjelenik a tesztkontakt és a tesztfelhasználó. A tesztcsoport nem. Oké, válasszuk a csoportot. Ekkor csak a tesztcsoport jelenik meg. Jó, akkor vegyünk fel két feltételt, legyen felhasználó is és csoport is. Ekkor nem jelenik meg semmi. Nyilván az idióta engine ‘és’ feltételt tett közéjük.
Khmm. Valami nem stimmel.
Jelöljük ki a harmadik fülnél csak a felhasználót és nézzük meg, milyen lekérdezést alkot a robot. Imhol.
(&
(&
(&
(&
(mailnickname=*)
(|(&(objectCategory=person)(objectClass=user)(|(homeMDB=*)(msExchHomeServerName=*)))
(&(objectCategory=person)(objectClass=contact))
(objectCategory=group)
)
)
)
(objectCategory=user)(extensionAttribute8=Valami*)
)
)
A valóságban nincs tördelve. Kell hozzá némi áttekintőképesség, szó se róla.
A ‘*’ az az aszteriszk, azaz jelentése: ‘bármi’. A ‘valami=*’ jelentése, hogy létezik-e egyáltalán a tulajdonság.
Tessék jobban átnézni. Where is the loony? A végén. Egyszerűen tök felesleges az (objectCategory=user) feltétel. Ez a hülye kattintógép viszont mindenképpen berakja, csak hol ‘user’, hol ‘group’ paraméterrel.
Ilyenkor vonja meg az ember a vállát. Hülyék vagytok. Majd beírom én a feltételt. Csakhogy a hülyeség ritkán áll meg félúton: a gyönyörű kattogtatógépben nincs custom fül; _nem lehet_ saját keresőfeltételt beadni. Ami már csak azért is meglepő, mert szószerint ugyanígy néz ki az AD custom query kattogtatógépe és ott meg van olyan: egyszerűen a legördülő menüben eggyel több a választható lehetőség és bejön egy üres szövegmező, ahová gépelhetsz.
Elég mélyre ástunk eddig is, itt az idő, hogy még mélyebbre hatoljunk. Nyilván ezek a lekérdezőfeltételek valahol le vannak tárolva az AD-ban is. Vegyük elő kedvenc AD matyizó svájcibicskánkat, az ADSIedit mmc snap-int. (Szintén support tools.) Nagyon remélem, hogy senkinek sem mondok azzal nagy újdonságot, hogy az Exchange organizáció összes konfigurációs beállítása az AD konfigurációs névterében van elrejtve, a Services/Exchange alatt. Nem kell sokáig túrni, hamar meglesznek a címlista objektumok is… és igen, az objektum ‘purportedsearch’ tulajdonsága rejti a keresőfeltételt. Hanyag eleganciával töröljük ki a felesleges kérdést, hajigáljunk pár percig dartsot, majd ha a replikáció megtette a dolgát, nézzük meg immár az ESM-ben a tesztcímlistát. Gyönyörű. Ott van a lekérdezésünk. Írjuk még be a description mezőbe, hogy aki a grafikus felületen módosítani meri a lekérdezést, annak letört kezét fogjuk a seggébe dugni.
Nyertünk.
Eltekintve attól az apróságtól, hogy a teszt címlista nem hajlandó megjelenni kliensoldalon. De ez apróság, összehasonlítva azzal a hátul motoszkáló rossz érzéssel, hogy ez nem frankó. Kurvára nem elegáns. Eleve fel kell tölteni az AD-t ezzel a Valami értékkel. Ha új felhasználót, kontaktot veszünk fel, tudni kell, hogy ezt az értéket is be kell írni, ha látni akarjuk a címlistában. Emberi munka. Hibalehetőség. Tré.

Ötlet2
Ha nemcsak ldp-ből nézzük meg az objektumot, hanem ADSIedit-ből is, egyszercsak szemünkbe tűnik egy canonicalName nevű tulajdonság. Hö. Mifene. Ebben pont az az útvonal van, hogy hol található az objektum. Háde a címek egy meghatározott OU-ba fognak pottyanni – tehát elegendő lenne egy ‘!canonicalName=Domain/OU/*’ feltétellel kiegészíteni a jelenlegi globális címlista lekérdezési feltételét és máris Bob lenne a bácsikánk. Nosza kimásoltam a globális címlista feltételét (még szerencse, hogy engedik használni a ctrl+C-t…), egy editorban hozzáfűztem a fenti feltételt és az egészet bevágtam ADSIedittel a tesztcímlista megfelelő tulajdonságába. Darts, teszt – és üres halmazt kaptam. Habár szeretek dartsozni, de szorított a határidő, így gyorsítottam a tesztelésen. Nem a címlistáknál vacakoltam, hanem a már korábban emlegetett AD custom search ablakban teszteltem a lekérdezést – itt egyből látom az eredményt és van beíróablak. A tesztcímlista lekérdezésével egyből hibaüzenetet dobott. Jó, hagyjuk a GAL-t. Összekattogtattam egy jó általános lekérdezést, az eredményt kimásoltam, editorban hozzátettem a szükséges feltételt és visszamásoltam a custom search mezőbe, és… nem fogadta el! Szószerint ez történt: kurzor be az ablakba, ctrl+v, egy villanás – és nem történt semmi. Nem került be a szöveg. Azannya. Biztos elrontottam valamit az editorban. Fogtam a kattogtatás adta eredményt és egyből visszamásoltam a szövegablakba. Villanás, üres ablak. Mifasz? Adjuk be cseppenként. Editorból kezdtem feltételenként visszamásolni és most már megjelentek a sorok. Szépen haladtam is, de az utolsó feltétel bemásolásakor újból jött a villanás. Bazdmeg. És akkor még messze nem adtam vissza az akkori hangulatomat. Az idióták lekorlátozták a szövegablakban a bevihető karakterek számát. Olyannyira, hogy begépeléssel nem tudom összerakni azt a lekérdezést, melyet kattogtatással igen.
Habár szó sem volt replikációról, de megint sürgős dartsozhatnékom támadt.
Jó, menjünk vissza az elejére. Tesztként beírtam a ‘!canonicalName=Domain/OU/*’ lekérdezést – és erre is hibaüzenet jött. Így már sokkal tisztább a helyzet. Valamiért ez a feltétel nem jó. Miután tíz percig vizsla szemekkel ellenőriztem betűről betűre, hogy nem gépeltem-e el valamit, elkezdtem végre gondolkodni. Hogyan is működik egy AD query? Hol keres?
Hoppá. Ez bizony nem az egész AD-t túrja át, helyette csak az index adatbázist piszkálja – azt az adatbázist, melyet a globális katalógusok kezelnek. Benne van ebben az adatbázisban a canonicalName tulajdonság?
Nézzük meg. Schema management, canonicalName – bizony, üres az összes checkbox. Hát ezért halt meg futás közben az összes lekérdezés.
Itt volt az idő konzultálni az ügyféllel. Előtte azért megkérdeztem a Microsoftot, hogy mekkora plusz terhelést fog okozni az AD-nak egy új tulajdonság felvétele a GC adatbázisba. Ugyanazt a választ kaptam, mint Arthur Dent a bulldózer előtt: “semekkorát”. Ügyfélnek elmagyaráztam a helyzetet, azt mondták, hajrá, felvehetem a tulajdonságot.
Megvártam a péntek délutánt, nekifeszültem a sémának, bekattintottam a ‘vedd fel az index adatbázisba’ checkbox-ot és a ‘replikáld a GC-k között’ checkbox-ot… majd elgyönyörködtem két hibaüzenetben, miszerint ez a tulajdonság a System Class-ba tartozik, tehát se nem indexeltethető se nem replikáltatható.
Csak.

Ötlet3
Más választási lehetőség híján maradtunk az első módszernél. Közben eszembejutott egy újabb komplikáció: kliensoldalon mindenki a globális címlistát használja; ha bevezetek egy új címlistát, akkor mindenkinél át kell állítgatni. Az ügyfél viszont határozottan irtózik a kliensoldali beavatkozásoktól.
Kellene egy újabb ötlet.
Nos, eddig is bátrak voltunk. Miért ne lehetnénk még bátrabbak? Módosítsuk a globális címlistát. A neve marad globális, de kibelezzük és az álca alatt egy nem globális címlista lenne, mely megegyezne azzal a címlistával, melyet most használnak. A tesztlistát meg átberhelnénk és az lenne a tulajdonképpeni globális címlista.
Az ötlet jó. Némi fennakadást okoz, hogy a globális címlista (GAL) nem módosítható. A grafikus felületről. De itt van a jó öreg ADSIedit, és természetesen a GAL-nak is megvan a ‘purportedsearch’ tulajdonsága. A lekérdezés kimásolása a GAL-ból, átmásolása a tesztcímlistába, kibővítése a plusz feltétellel és visszamásolása a GAL-ba alig volt öt perc.
Csak hogy töltsem a helyet, idemásolom:
(& (extensionattribute2=Valami)(mailnickname=*)
(| (&(objectCategory=person)(objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))
(&(objectCategory=person)(objectClass=user)(|(homeMDB=*)(msExchHomeServerName=*)))
(&(objectCategory=person)(objectClass=contact))
(objectCategory=group)(objectCategory=publicFolder)(objectCategory=msExchDynamicDistributionList)
))
Enyhe szépséghiba, hogy mindez kliens oldalon egyelőre nem látszik.
De ráérünk, mert még fel kell tölteni az AD-t szkriptből és ez sem apró feladat. Először is figyelni kell arra, hogy a nemlétező tulajdonság és az üres tulajdonság nem ugyanaz, dacára, hogy lekérdezéskor ugyanazt a választ kapjuk az Isempty() függvénnyel. Habár van valami módszer – lekérdezve az Err értékét -, de nekem ez sohasem működött. Viszont jelen esetben igen fontos rögtön az elején különbséget tenni a két lehetőség között, ha ugyanis egy olyan objektum Exchange tulajdonságának adunk értéket, mely se nem mail-, se nem mailbox-enabled, akkor egy olyan szürke zónába léptünk, melytől minden tisztességes adminisztrátor irtózik. Meg a tisztességtelenek is. A zóna kizárólag a hülyéknek van fenntartva.
Szerencsére van egy másik tulajdonság, melynek mindig van értéke, ha az objektum mail/mailbox-enabled: ez a proxyaddresses tulajdonság. Ez gyakorlatilag egy tömb, amelyben az objektum összes e-mailcíme van felsorolva – márpedig egy darab mindenképpen van neki.
A többi már nem nagy ügy. Még arra kell figyelni, hogy a romantikus nevű MsExchHideFromAddressLists változó értéke hamis legyen – azaz az objektum ne legyen kirejtve a címlistákból.
Mehet is. De még mennyire. És de még hányszor. Eszméletlen eldugott sarkai lehetnek egy 3 fás 6 tartományos erdőnek, zegzugos OU-kal, konténerekkel – márpedig addig kell erőszakolni a szkriptet, amíg a globális címlistával megegyező számú objektumot kapunk vissza. (Igen, megírhattam volna rekurzívnak is. Későn jutott eszembe.)

De végül ez is rendbe jött. Ennek ellenére kliensoldalon semmi változás. A tesztgépen továbbra is a régi címlisták látszanak.
Itt van egy láb, kibe rúgjak? Ki mögött rejtőzködik az MSExchangeAL? A legvégén biztos a System Attendant lesz, de azért durva lenne az egész szervert restartolni egy frissítés miatt.
Miről is írtam az elején? Hogy a listatagság beleíródik az objektumba. Ez egy bulk jellegű írási művelet. Melyik komponens lát el hasonló funkciót? Igen, a Recipient Update Service, a jó öreg RUS. Haza is érkeztünk. Ugyanazt kell csinálnunk, mint amit egy új recipient policy bevezetésekor: minden tartományi RUS-ba bele kell rúgni egy jó nagyot. Figyelem: nagyot. Én úgy tapasztaltam, hogy a hivatalos véleménnyel szemben kicsi rúgásra a füle botját sem mozgatja. (Segítség. Kis rúgás: update. Nagy rúgás: rebuild.)
Egy-két óra várakozás következett, közben volt egy röpke őszülés is: ugyanis befigyelt egy jó hosszú periódus, amikor üres volt a teljes globális címlista. Roppant lehangoló látvány. Szerencsére nem sokan látták, ez ugyanis már péntek éjfél körül volt. Hogy a modemesek mennyit fognak átkozódni, amíg az új offline Address Book-ok leérnek hozzájuk, azt nem tudom. Remélem, nem találnak meg.

Tulajdonképpen most már készen is vagyunk, boríthatják a címeket.
És ha már itt lesznek, át kell túrnom alaposan a tulajdonságaikat, hátha találok valami különbséget, ami alapján automatizálható lehet a listázás.