piątek, 23 lipca, 2010 - 12:27

Backup ISA TMG

ISA i TMG to takie fajne serwerki, które łatwiej jest postawić od początku niż przywrócić z kopii zapasowej. A jeszcze prościej zrobić jeden obraz z systemu, a następnie odtworzyć z obrazu, i wgrać najnowszy konfig. Ale jak więc zabezpieczać konfig ISA/TMG? Dość prosto i szybko można to zrobić skryptem, pamiętając o kilku rzeczach:

  • Backup konfiguracji powinien być trzymany na oddzielnym serwerze, najlepiej backupowanym przez coś innego
  • Pojedyńcza kopa zapasowa ma 7-8 MB, co przy kopii codziennej daje 3G na rok, a tygodniowej 420MB
  • Folder docelowy dobrze jest skompresować, wybierając odpowiednią flagę NTFS-ową na folderze, wtedy ilość miejsca z 7MB spada do 2.5MB, przy czym kolejne backupy będą zabierały jeszcze mniej
  • Jeśli certyfikaty będą eksportowane, to znajdą się również w konfiguracji TMG
  • Przy tmg, routing jest trzymany w konfiguracji TMG, więc przed importowaniem trzeba go wyrzucić z pliku netsh

A poniżej skrypt:

‘bAckupisa/tmgtofile
Const destination = “C:\bck\”
Const passwd = “12345678″

Const fpcExportImportPasswords = &H00000001
Const fpcExportImportUserPermissions =&H00000002
Const fpcExportImportServerSpecific= &H00000004
Const fpcExportImportEnterpriseSpecific = &H00000008
      fpcOptionalData = fpcExportImportPasswords Or _
      fpcExportImportUserPermissions Or _
      fpcExportImportServerSpecific Or _
      fpcExportImportEnterpriseSpecific
date = replace(Date,“/”,“_”)
Set root = CreateObject(“FPC.Root”)
Set firewall = root.GetContainingArray
firewall.ExportToFile destination & date & “_ISA_CONFIG.XML”_
                     ,fpcOptionalData,passwd

Set wshShell = WScript.CreateObject(“WScript.shell”)
wshshell.run “cmd/c “” netsh int ip dump > ” & _
              destination & date & “ip.nsh”"”
wshshell.run “cmd/c “” netsh int ip show dns > ” & _
             destination & date & “dns.txt”"”

wtorek, 13 lipca, 2010 - 13:59

pedanteria niepożądana

Autor: nExoR | Kategorie: script/developer

prosty search po obiektach w AD, które mają włączoną flagę niewygasania hasła:

$searcher = New-Object DirectoryServices.DirectorySearcher([ADSI]LDAP://OU=PL,OU=Country,DC=domain,DC=test)
$searcher.filter = (&(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=65536))
$searcher.propertiesToLoad.add(sAMAccountName)
$searcher.propertiesToLoad.add(displayname)
$searcher.propertiesToLoad.add(sn)
$searcher.propertiesToLoad.add(distinguishedname)
$s=$searcher.findall()
echo *************************

foreach($a in $s) {
    $i=$a.Properties
    echo [$($i.sAMAccountname)] , $($i.displayName) , $($i.sn), $($i.distinguishedName)
}

i okazuje się, ze na ekranie pustka… wyświetlają się tylko pola ‘sn’. WTF?

problemem jest pedanteria i używanie małych/wielkich znaków – okazuje się, że PS przyjmuje wyłącznie małe literki:

echo "[$($i.samaccountname)] , $($i.displayname) , $($i.sn), $($i.distinguishedname)"

pffff…

eN.

poniedziałek, 12 lipca, 2010 - 21:13

BCD i skrypty

Autor: domel | Kategorie: article, script/developer, windows

Totalny w-file tym razem. Tyle, że rozwiązany :)

Problem – zautomatyzować operację dodawania wpisów w BCD, żeby skryptem dodawać Pingwina do boot menu w Windows 7.

Pierwszy krok prosty: dodajemy wpis dla grub4dos - http://grub4dos.sourceforge.net/wiki/index.php/Grub4dos_tutorial#Booting_GRUB_for_DOS_via_the_Windows_Vista_boot_manager

Teraz wypada by to przenieść na inną maszynę – chwilka przekopywania się przez dokumentację i okazuje się, że BCDEdit umożliwia import i export ustawień. piknie – robić backup i potem go odzyskać na innej maszynie. Niestety nie działa. Z tego prostego powodu, że wpis dla W7 nie ma GUID {current}, tylko jakiś dziwoląg – niestety GUIDy są generowane pseudolosowo. Efekt: pingwin się podnosi, Windows nie ^^

Rozwiązania są dwa:

1. Wykorzystać WMI i BCDProvider (http://msdn.microsoft.com/en-us/library/aa362675(v=VS.85).aspx) i można zrobić tak (wymaga wcześniejszego dodania wpisów):

set args = WScript.Arguments
num = args.Count

if num <> 1 then
  WScript.Echo Usage: CScript \\nologo [Menu Selection]
  WScript.Quit 1
end if

MenuPick=args.Item(0)

Const BcdLibraryString_Description = &h12000004
Const BootMgrId = {9dea862c-5cdd-4e70-acc1-f32b344d4795}
Const DefaultType = &h23000003
Const WindowsImages = &h10200003

strComputer = .
Set objStoreClass = GetObject(winmgmts:{(Backup,Restore)}\\ & _
strComputer & \root\wmi:BcdStore)
objStoreClass.OpenStore “”, objStore
objStore.EnumerateObjects WindowsImages, colObjects
For Each objObject in colObjects
  objObject.GetElement BcdLibraryString_Description, objElement
  If Instr(objElement.String, MenuPick) Then
    objStore.OpenObject BootMgrId, objBootMgr
    objBootMgr.SetObjectElement DefaultType, objObject.ID
  End If
Next

2. wykręcić pętlę w CMD:

for /f tokens=3 %%a in (’bcdedit -create -d PJWSTK Linux  -application bootsector’) do set guid=%%a
bcdedit /set %guid% device boot
bcdedit /set %guid% path \grldr.mbr
bcdedit /displayorder %guid% /addlast

działa pięknie – po prostu pobiera trzecie słowo z outputu BCDEDIT – to ZAWSZE jest GUID :)

teraz można to wrzucić jako cmd /c “%scriptroot%\bcd.cmd” do Task Sequence w MDT i mamy zautomatyzowane dodawanie pingwinów na W7 :D

PS. warto pamiętać, ze sysprep kasuje zawartość BCD :)

piątek, 2 lipca, 2010 - 16:49

ping to file

Autor: nExoR | Kategorie: script/developer

taka mała piątkowa gimnastyka.

krótki kod, powalający sprawdzić czy host żyje i zapisać wyniki do pliq.

Get-Content .\hostyerr.txt | %{ $out=`ping $(($_).trim()) -n 1` ; if( ($out[5]).contains("0% loss")) {echo $_}} >hostsok

nie jest to rozwiązanie idealne, ale co fajnego:

  • dla zmiennej można przypisać wynik działania programu – używa sie tych ciapek w lewym-górnym rogu [jak się nazywają?]
  • znów wszystko można zrobić w jednej linijce.

zamiast ‘contains’ lepiej używać –match ale to kiedyś indziej bo…

czas na weekend.

eN.

wtorek, 25 maja, 2010 - 17:50

wsh.run

Autor: nExoR | Kategorie: script/developer, tips'n'tricks

prosty scenariusz: skrypt logowania, uruchamiający bginfo. podstawowy problem w vbs: obsługa katalogów ze spacją. niestety przekazanie “c:\program files\systeinternals\bginfo.exe” z podobnie wyglądającymi parametrami jest upierdliwe – ile tych cholernych cudzysłowów jest potrzebne?

WShell.run """"&SUPPORT_DIR&"bginfo.exe"" """&SUPPORT_DIR&"wrkstations.bgi"" /accepteula /timer:0"

a cały skrypcik wygląda tak:

********************************************************************************  prepare server environment - processexplorer and bginfo on servers         **                                                                             **    author:         nexorek[at]gmail.com                                     **                             last change  25.o5.2kd                          **                                                                             ********************************************************************************

OPTION EXPLICIT
ON ERROR RESUME NEXT
CONST SERVER_DIR=\\FILESERVER\gpofiles$\
Dim SUPPORT_DIR
Dim FSO, file
Dim WShell

********************************************************************************                                 MAIN                                        ********************************************************************************
set FSO=CreateObject(Scripting.FileSystemObject)
Set WShell = WScript.CreateObject(WScript.Shell)
SUPPORT_DIR=WShell.ExpandEnvironmentStrings(%programfiles%)&\sysinternals\
check admin directory
if not FSO.FolderExists( SUPPORT_DIR ) then
  FSO.CreateFolder( SUPPORT_DIR )
end if
copy if not exist
iFCopy(bginfo.exe)
iFCopy(wrkstations.bgi)
iFCopy(procexp.exe)
WShell.run “”&SUPPORT_DIR&bginfo.exe”" “”&SUPPORT_DIR&wrkstations.bgi”" /accepteula /timer:0

********************************************************************************                              SUBS’N'FUNCS                                   ********************************************************************************
Sub iFCopy(sourceFile)
  if not FSO.FileExists( SUPPORT_DIR&sourceFile ) then
    FSO.copyFile (SERVER_DIR&sourceFile), SUPPORT_DIR, true
  else
   check version
    if strcomp( FSO.getFileVersion(SERVER_DIR&sourceFile),FSO.getFileVersion(SUPPORT_DIR&sourceFile) )<>0 then
      FSO.copyFile (SERVER_DIR&source), SUPPORT_DIR, true
    end if
  end if
End Sub

eN.

czwartek, 25 marca, 2010 - 17:57

mapdrive script:automatyzacja mapowania dysków z serwera plików

prosta idea [nie zawsze wykonalna] – wedle podręcznikowego przydzielania uprawnień i zarządzania grupami polega na tym, że dla każdego udziału na FS tworzona jest grupa security-domain local – np. Share_FS01_RW_public. tej grupie nadawane są uprawnienia [w tym przypadq RW] a całe zarządzanie przydzielaniem uprawnień polega na dodaniu grupy funkcyjnej do grupy dostępowej. przykład w praktyce:

  1. zakładam w AD jednostkę organizacyjną OU=AccessGroups
  2. zakładam w AD jednostkę organizacyjną OU=Accounting
  3. zakładam grupę funkcyjną security-global ‘Accounting’ i dodaję odpowiednich userów). te 3 kroki oczywiście definiują miniaturkę podstawowego środowiska lab
  4. księgowość musi mieć swój prywatny katalog na FS więc:
    1. zakładam grupę Security-Domain Local o nazwie “AG_FS01_RW_Accounting” w OU-AccessGroups – to przykładowa notacja która pozwala w łatwy sposób odróżnić grupy dostępowe od innych, zawiera nazwę serwera, którego dotyczy, uprawnienia [dzięki temu można łatwo odróżniać grupy RW od R] oraz jakiego udziału dotyczą.
    2. zakładam katalog ‘Accounting’ na FS01 i publiqję go jako ‘\\FS01\Accouting’
    3. jedyne uprawnienia jakie zakładam na katalogu to “authenticated users:M” na poziomie udziału oraz “AG_FS01_RW_Accounting:RW” na poziomie NTFS
    4. teraz aby nadać uprawnienia do katalogu wystarczy, że dodam grupę ‘Accounting’ do ‘AG_FS01_RW_Accounting’

wadą takiego rozwiązania jest niezliczona ilość grup w złożonym środowisq – a więc czasem podręcznikowe rozwiązanie nie może być zastosowane. zalet jednak jest bardzo dużo:

  • wszystko zarządzane z jednego miejsca za pomocą ADUaC bez potrzeby logowania/sprawdzania na serwerach plików
  • ..czyli centralizacja zarządzania uprawnieniami
  • łatwość delegacji zarządzania uprawnieniami
  • utrzymywanie spójnej, prostej struktury: przejrzystość
  • prostota automatyzacji mapowania: ujednolicone/uniwersalne skrypty mapowania
  • automatyczne mapowanie dysków dla użytkowników zaraz po dodaniu do grupy funkcyjnej

poniżej zamieszczam przykładowy skrypt logowania mapujący dyski, który ma możliwość mapowania na podstawie przynależności do grupy. ponieważ użytkownicy nie należą bezpośrednio do grupy dostępowej, sprawdzany jest drugi poziom zagnieżdżenia – atrybut memberof. jest możliwość włączenia rekursywnego sprawdzania zagnieżdżenia ale ma to kilka mankamentów:

  • jest dość powolne w realnym środowisq, gdzie grup jest sporo
  • istnieje niebezpieczeństwo przypadkowego zmapowania katalogu z powodu powiązań pomiędzy grupami

******************************************************************************** map network drives lib for logon script                                     **   can be used to map multiple drives. possible error handling               **   user must have proper permissions - providing credentials not handled     **                    nexorek(at)gmail.com           25.iii.2kd                **EXAMPLE USE:                                                                 **addMapping “m”,”\\192.168.1.100\c$”                                          **addMapping “n”,”\\192.168.1.100\d$”                                          **mapNetworkDrives                                                             **                                                                             **EXECUTE WITH ERROR HANDLING:                                                 **addMapping “m”,”\\192.168.1.100\c$”                                          **addMapping “n”,”\\192.168.1.100\d$”                                          **if mapNetworkDrives<>0 then                                                  ** wscript.echo “do something with “&mapNetworkDrives                          **else                                                                         ** wscript.echo “netdrives mapped successfully”                                **end if                                                                       **                                                                             **MAP USER-named DRIVE                                                         **addMapping “h”,”\\server\share\”&logonName                                   **                                                                             **CONDITIONAL MAP DRIVE ON USER MEMBERSHIP                                     **if isMember(”group name”) then addMapping “s”,”\\server\sharename”           **                                                                             ********************************************************************************
ON ERROR RESUME NEXT
Const FileServer=\\put.servername.here

Dim dictMappings
Dim oADSystemInfo
Dim oUser, groupList, loginName, dictGroups, g
dim tempname

********************************************************************************                                 MAIN                                        ********************************************************************************used for store information about mapping drives
set dictMappings=CreateObject(Scripting.Dictionary)

Object for user/computer information
set oADSystemInfo=CreateObject(ADSystemInfo)
USER
set oUser=GetObject(LDAP://&oADSystemInfo.UserName)
loginName=oUser.SAMAccountName
USER GROUPS MEMBERSHIP
set dictGroups=CreateObject(Scripting.Dictionary)
groupList=oUser.getEx(memberOf)
for each g in groupList
  if not strcomp(lcase( left( getObject(LDAP://&g).objectCategory,8) ),cn=group ) then
    enumGroupsInGroup(LDAP://&g)
  end if
  tempname=lcase(mid(g,4,InStr(g,,)-4))
  if not dictGroups.exists(tempname) then dictGroups.add tempname, g
next

PUT MAPPINGS HERE. example:
if isMember(AG_FS01_RW_Accounting) then addMapping i,\\FS01\Accounting
mapNetworkDrives

********************************************************************************                              SUBS’N'FUNCS                                   ********************************************************************************
Sub addMapping(drvLetter, srvPath)
  dictMappings.add ucase(drvLetter)&:, srvPath
End Sub

Function mapNetworkDrives()
  ON ERROR RESUME NEXT
  Dim wshNetwork
  Dim AllDrives, i

  Set WshNetwork = WScript.CreateObject(WScript.Network)
  Set AllDrives = WshNetwork.EnumNetworkDrives()

  For each i in dictMappings
    unmap before mapping - just to be sure that drive letter is not used
     if dictMappings.Exists(i) then
       WShNetwork.RemoveNetworkDrive AllDrives.Item(i)
     end if
    WShNetwork.MapNetworkDrive i, dictMappings(i)
    mapNetworkDrives=cstr(hex(err.number))
  next
End Function

Function isMember(gName)
  if dictGroups.Exists(lcase(gName)) then
    isMember=TRUE
  else
    isMember=FALSE
  end if
End Function

Sub enumGroupsInGroup(gname)
  ON ERROR RESUME NEXT
  Dim grpList, gentry

  grpList=getObject(gname).getEx(memberOf)
  for each gentry in grpList
    if not strcomp(lcase( left( getObject(LDAP://&gentry).objectCategory,8) ),cn=group ) then
      dictGroups.add lcase(mid(gentry,4,InStr(gentry,,)-4)), gentry
      next line is recursion for multilevel nesting. for performance purposes assumed 2-level structure
      so it is disabled. there should be no more then second-level nesting for permissions
      in fact it can be even dengerous - someone may have accidently mapped drive  
      enumGroupsInGroup(”LDAP://”&gentry)
    end if
  next

End Sub

jak się okazuje, czasem teoria przekłada się na praktykę – mówię tu o podręcznikowym projektowaniu grup (global/domain local i standardowe AGDLP).

eN.

niedziela, 28 lutego, 2010 - 11:29

Ciężkie życie programisty ….

Autor: wAsyL | Kategorie: joke, script/developer, technology

Żart branżowy, na deszczową niedzielę {przynajmniej u mnie :-/}

Ciężkie życie programisty

czwartek, 11 lutego, 2010 - 13:19

Usuwanie starych kont komputerów z Active Directory

Kiedys do wywalania starych i nieużywanych kont komputerów z AD służył mi taki znaleziony w sieci skrypt:

 

dsquery computer -inactive 16 -limit 0 | dsrm -c -noprompt

 

ale coś przestał działać na Win2008.

Nie wnikając w szczegóły i chcąc iść  z duchem czasu przerobiłem go na PowerShella

 

Get-QADComputer -IncludedProperties pwdLastSet -SizeLimit 0 |where {$_.pwdLastSet -le (Get-Date).AddDays(-180) } | Remove-QADObject -DeleteTree  -Force

 

Może komuś się przyda przy porządkach w AD.

wtorek, 9 lutego, 2010 - 17:54

PS: jak usunąć elementy z listy oddzielanej średnikami

Autor: nExoR | Kategorie: script/developer

tak na prawdę to kolejna lekcja zabawy hashtablami i ciągami. scenariusz w moim przypadq był taki, żeby zweryfikować jakie konta należą do grupy lokalnych adminów na stacjach roboczych. skrypt logowania wrzucił zawartość grupy do bazy danych, potem pobrałem sobie dane do zmiennej $DataSet.Tables[0] . teraz trzeba wygenerować raport “wypisz wszystkie komputery, na których ktoś należy do adminów a nie powinien, z informacją jakie to konto”. rozwiązanie jest takie:

#connection objects
$sqlconnection=New-Object system.data.sqlclient.sqlconnection
$sqlcmd= New-Object System.Data.SqlClient.SqlCommand
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$DataSet = New-Object System.Data.DataSet

#connect MYDATABASE on MYSQLSERVER
$sqlconnection.ConnectionString=Server=MYSQLSERVER;database=MYDATABASE;Integrated Security=True
$sqlcmd.CommandText=select * from TABLENAME
$sqlcmd.Connection=$sqlconnection
$SqlAdapter.SelectCommand = $SqlCmd
#that is strange way of getting results - they are popullated into $dataset array. 
$SqlAdapter.Fill($DataSet)|out-null

#now when we have everything in an array so we can manipulate on data
#first define hashtable for computers with admin accounts
$unsecure=@{};
foreach ($row in $DataSet.Tables[0]) {
    #how to remove correct group/user names from check? one can try to 
    #operate on strings but it’s so… bueee
    #it’s easier and nicer to have an object for comparison. hashtable is perfect
    $splitrow=@{};

    #column 1 in my database contains memberof information separated with semicolon
    #f.ex. ‘domain admins;administrator;non-admin-user;’
    #so change this string into a hashtable:
    $row[1].split(;)|%{$splitrow.add($_,0)}
    #the second parameter is obligatory to fill but tottaly unimportant in this situation. 
    #so i put ‘0′ but could be anything.
    #now it’s easy to remove correct entries which are not interesting for this task
    $splitrow.Remove(administrator);
    $splitrow.Remove(Domain Admins);
    $splitrow.Remove(grupa_operatorow);
    $splitrow.Remove(specjalna_grupa);
    #as there is ‘;’ on the end it will always produce one additional, empty record. 
    $splitrow.Remove(“”);

    #if there are any unwanted logins, the number will be greater then 0
    if ($splitrow.count -gt 0) {
        #add entry to final table. i want to have it back as a string so -join is going to be helpful
        $unsecure.add($row[0],$splitrow.keys -join ;)
    } else {
        #out of interest - just informational
        echo KOMPUTER: $($row[0]) jest ok;
    }

} 

#now print information to a file
$unsecure.count|out-file c:\temp\admins.txt
$unsecure.getEnumerator()|Sort-Object name|out-file c:\temp\admins.txt -append

czego można się nauczyć z tego skryptu?

  • jak połączyć się z bazą
  • jak pobrać i operować na wynikach
  • jak radzić sobie z hashtablami
    • szczególnie ciekawy jest sposób sortowania – gdzie użyty jest getEnumerator. zwykłe $unsecure|Sort-Object nie zahula

sposób być może nie jest optymalny więc będę wdzięczny za sugestie i alternatywne rozwiązania (:

eN.

poniedziałek, 8 lutego, 2010 - 17:03

Dictionary object dla powershell czyli Hashtable

Autor: nExoR | Kategorie: script/developer

w PS zamiast obiektu słownikowego jest hashtable. Po przejrzeniu wielu wygooglanych przykładów oczywiście nie znalazłem odpowiedzi na moje pytania:

  • co jeśli wartością ma być zmienna tablicowa?
  • jak dostać się do takiej wartości?
  • jak zrobić enumerację z możliwością wykonania działań?

odpowiedzi są dość intuicyjne. nie ma problemu, żeby do hashtabla dodać tablicę jako wartość, a następnie robić jakieś operacje. enumeracja jest dokładnie taka sama jak w VBS:

#declaration
$hashtable=@{ aaa=@(0,0); bbb=@(0,0) }
#print out single value
$hashtable[aaa][1]++;
echo $hashtable[aaa][1]; #out -> 1
#enum
$hashtable
#enum with some operation
$hashtable[aaa]=@(5,10)
$hashtable[bbb]=@(3,1)
foreach($entry in $hashtable.keys) {
    echo $($hashtable[$entry][0]/$hashtable[$entry][1]*100)%;
} # out -> 300% 50%

póki co nie bawiłem się $hashtable.getEnumerator() – być może da się zoptymalizować to trochę.. ale działa wszystko bez problemu niemal tak samo jak VBSowy Dictionary (: listy są w .keys i .values, testy są metodami .containsKey i .containsValue, dodawanie .add, usuwanie .remove – różnice w wykorzystaniu są niewielkie (:

eN.

środa, 3 lutego, 2010 - 14:09

modyfikacja grup mailowych via powershell

q pamięci, odnośnie poprzedniego wpisu:


$searcher=[ADSISearcher]LDAP://DC=domain,DC=name
$searcher.Filter=(&(ObjectClass=group)(name=STR*)) //wszyskie grupy zaczynajace sie na dany ciąg
$searcher.SearchRoot=OU=mail groups,OU=my ou,DC=domain,DC=name
$res=$searcher.FindAll()
foreach ($r in $res) {
    $grp=[ADSI]$r.path;
    $grp.mail=$($grp.mailnickname)@domain.name;
    $grp.putex(2,proxyAddresses,@(SMTP:$($grp.mailnickname)@domain.name));
    $grp.setinfo()
}



POWERSHELL RLZ! q:

*UPDATE

zostałem poproszony o małe wyjaśnienia niniejszym jakie ciekawostki można wyłapać dla początqjących. samych poleceń ADSI nie będę wyjaśniał, sqpię się na składni PS.

jednym z podstawowych problemów na początq sprawiają najprostsze rzeczy – np. wypisywanie na ekran. z punktu widzenia składni najciekawsze polecenie to “$grp.putex(2,"proxyAddresses",@("SMTP:$($grp.mailnickname)@domain.name"));”. postaram się wyjaśnić co tu się dzieje.

wypisanie zmiennej na ekran jest proste:

echo $zm

ale przy dwóch już robi się dziwnie, ponieważ:

echo $zm1 $zm2

wypisze obie.. ale w oddzielnych liniach. znakiem konkatenacji jest niby ‘+’ ale dodanie

echo $zm1 + $zm2

spowoduje wypisanie 3ech linii – gdzie ‘+’ jest normalnym znakiem. odpowiedz jest dość prosta: wystarczy zamknąć zmienne w cudzysłowach:

echo “$zm1 $zm2”

można też zastosować zapis znany z c++:

echo (“{0} {1}” –f $zm1,$zm2)

ciekawie się robi, kiedy wartość ma być  lub wartością atrybutu obiektu. PS po ‘$’ automatycznie kończy wyliczanie po pierwszym znaq specjalnym – np. kropka. w przedstawionym skrypcie wypisać należy wartość atrybutu ‘mailnickname’. wypisanie go w ten sposób:

echo “$grp.mailnickname”

wypisze $grp a na koniec doda ciąg “.mailnickname”. w związq z tym trzeba zadeklarować zmienną tymczasową, przyjmującą wartość wyliczaną:

echo “$($grp.mailnickname)”

trochę o ADSI musi być – ‘putex’ służy do ustawiania atrybutów wielowartościowych i przyjmuje 3 parametry. ten trzeci z nich to ostateczna wartość – zasady są dokładnie takie same jak przy wypisywaniu na ekran – przedstawione powyżej. dodatkowo, ponieważ atrybut jest wielowartościowy, musi to być tablica. stąd cała postać zmiennej przekazywanej do “putex” musi wyglądać tak:

@("SMTP:$($grp.mailnickname)@domain.name")

gdzie ‘@’ oznacza deklarację tablicy, potem jest ciąg, który zawiera jedną wartość wyliczaną: $($grp.mailnickname) . przykładowy output: [jednoelementowa tablica] “SMTP:nexor@domain.name”

eN.

środa, 16 grudnia, 2009 - 14:12

wypisywanie wartości pliku - powershell

Autor: nExoR | Kategorie: script/developer

muszę to sobie zapisać, bo to prościutkie zadanie po raz eNty zabiera mi czas.. [”myśl inaczej, myśl inaczej…”]

zadanie proste: odczytać wartości z pliq textowego i coś z nimi zrobić - w najłatwiejszej postaci wypisać na ekran, czyli musi to być string. najlogiczniejszą strukturą jaka mi przychodzi zawsze do głowy jest:

Get-Content .\text.file | echo $_

co przysparza mi masę problemów ponieważ dla każdej odczytanej wartości dostaję piękny komunikat błędu. następnym krokiem była próba skonwertowania otrzymanej wartości na String.. no i to jest właśnie złe myślenie - ponieważ to, co jest przepipowane [LOL może przepajpowane? (; ] to obiekt listy - a nie kolejne wartości. w związq z tym zamiast robić operacje na otrzymanej zmiennej ($_) trzeba zrobić dla niej enumerację:

Get-Content .\text.file | %{echo $_}

no… teraz w końcu zapamiętam q:

ps. pełne zadanie: plik z nazwiskiem i imieniem. otrzymać listę emaili:


PS P:\> Get-Content .\USERS.txt | %{dsquery user "OU=AA,OU=BBB,DC=DOMAIN,DC=local" -scope subtree -name "$_"} | %{ $info=dsget user "$_" -email; echo $info[1].trim()}

eN.

poniedziałek, 7 grudnia, 2009 - 9:54

Ku pamięci – usuwanie Forefront Client Security

Taki mały startup script do usuwania FCS-a ze stacji:

if not exist C:\UninstallFCS md C:\UninstallFCS 

rem uninstall Microsoft Forefront Client Security Antimalware Service
MsiExec.exe /X {D3E31640-DC20-4722-A1CF-604FF6C540B0} /norestart /qn /l*v C:\UninstallFCS\UninstallFCSAM.LOG 

rem uninstall Microsoft Forefront Client Security State Assessment Service
MsiExec.exe /X {E8B56B38-A826-11DB-8C83-0011430C73A4} /norestart /qn /l*v C:\UninstallFCS\UninstallFCSSSA.LOG 

rem uninstall Microsoft Operations Manager 2005 Agent
MsiExec.exe /X {F692770D-0E27-4D3F-8386-F04C6F434040} /norestart /qn /l*v C:\UninstallFCS\UninstallMOM.LOG

Przy czym trzeba pamiętać, żeby odfiltrować/odpiąć polisę, która go wcześniej zainstalowała.

środa, 16 września, 2009 - 19:00

AD Powershell na WS2003/2008 – teraz to już możliwe :)

Autor: domel | Kategorie: news, script/developer, server

Jakiś czas temu pisałem o Active Directory Management Gateway Service. Teraz już jest :)

Link do pobrania: http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=008940c6-0296-4597-be3e-1d24c1cf0dda

poniedziałek, 14 września, 2009 - 12:02

Usuwanie nie skasowanych profili

Idea profili mandatoryjnych jest piękna. Niestety czasami skubańce nie chcą się kasować :/ Takimi cudeńkami obdarzamy studentów, żeby jak im się coś popsuje wystarczył restart i mają świeży profil. Aby sam mechanizm wspomóc wrzuciłem do GPO taki prościutki skrypcik:

on error resume next
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder("C:\Documents and Settings\")
Set colFOlders = objFolder.SubFolders
Dim re 
set re = New RegExp
re.Pattern = "(^s[0-9][0-9][0-9][0-9]?[0-9])|(^pd[0-9][0-9][0-9])"
For Each subFolder In colFOlders
 
if re.Test(subFolder.Name) then 
   
subFolder.delete(true)    
 
end if
Next
wtorek, 25 sierpnia, 2009 - 19:05

FTM nie usuwa kolejki

Autor: nExoR | Kategorie: script/developer

ten stary wpis to jeden z najczęściej przeze mnie wyszukiwanych na w-files. a więc zamieszam nieco uaktualnioną wersję dla powershell’a:

remove-item "$env:USERPROFILE\appdata\roaming\microsoft\File Transfer Manager\ftmTransferList*.*"

remove-item "$env:USERPROFILE\appdata\roaming\microsoft\File Transfer Manager\RequestQueue" –Force -Confirm

n.

poniedziałek, 10 sierpnia, 2009 - 16:08

SCCM Console Extensions – WOL nie działa na WS2008

Ostatnio mocno zainteresowałem się rozszerzeniami konsoli SCCMa. Ogólnie polegają one na fajnym wykorzystaniu przestrzeni nazw WMI, którą daje ConfigMgr. Ale żeby nie malować trawy na zielono najpierw zainstalowałem 2 pakiety wtyczek – SCCM Right Click Tools oraz SCCM Console Extensions. Po ich przetestowaniu muszę powiedzieć, że można robić niezłe cuda w MMC przy użyciu hta i vbs ;)

Tyle, że jedna funkcjonalność mi nie działała – Wake On Lan. Wiem, że komputery mają uruchomiony remote wake up i są podłączone do sieci (1 warstwa) mam możliwość directed broadcast (2. warstwa), więc problem musi być z działaniem samej wtyczki. Po kliknięciu tutaj:

image

Mamy śliczny komunikat, że wszystko się udało.

image

grzebiąc w kodzie znajdujemy coś takiego:

errReturn = WshShell.Run(chr(34) & strCurrentPath & "WOL.exe" & chr(34) & " " & strWOLAddress,0)
If errReturn <> 0 Then
   
LogArea.Value = LogArea.Value & "ERROR!" & vbCrLf
   
LogArea.Value = LogArea.Value & " " & Err.Description & vbCrLf
   
LogFile.WriteLine(Date & " " & Time & vbTab & "Send Wakeup: ERROR!" & vbTab & Err.Description)
   
LogArea.scrollTop = LogArea.scrollHeight
Else
   
WolSuccess = 1
   
LogArea.Value = LogArea.Value & "Wakeup sent to " & strMACAddress & vbCrLf
   
LogArea.scrollTop = LogArea.scrollHeight
   
LogFile.WriteLine(Date & " " & Time & vbTab & arrComputers(iList) & ": Wakeup sent to " & strMACAddress)
End If

Widać, że to nic innego niż wywołanie wol.exe z katalogu z rozszerzeniami. Sprawdzamy co się dzieje z tym programikiem. Po wpisaniu w CMD – "c:\Program Files\SCCMConsoleExtensions\WOL.exe" 00219B7B80CD też działa… ale ale – brakuje adresu IP i maski do kierunkowego bradcastu. Patrzymy wol.exe /? można podać tylko IP :/ niedobrze. sieci z nie klasowymi maskami odpadają. po chwili szperania w bingu znalazłem narzędzie wolcmd.exe. Usage: wolcmd [mac address] [ipaddress] [subnet mask] [port number]. No i pięknie teraz tylko wrzucić je do odpowiedniego katalogu i podmienić kawałęk skryptu na coś takiego:

strNetmask = inputbox("Provide netmask")
For Each instance in colMACAddress
   
strMACAddress = instance.MACAddress
   
strWOLAddress = (Replace(instance.MACAddress,":",""))
   
strIPAddress = instance.IPAddress
   
LogArea.Value = LogArea.Value & arrComputers(iList) & ": "
   
LogArea.scrollTop = LogArea.scrollHeight
   
errReturn = WshShell.Run(chr(34) & strCurrentPath & "wolcmd.exe" & chr(34) & " " & strWOLAddress & " " & strIPAddress & " " & strNetmask & " 7",0)
    
   
If errReturn <> 0 Then
       
LogArea.Value = LogArea.Value & "ERROR!" & vbCrLf
       
LogArea.Value = LogArea.Value & " " & Err.Description & vbCrLf
       
LogFile.WriteLine(Date & " " & Time & vbTab & "Send Wakeup: ERROR!" & vbTab & Err.Description)
       
LogArea.scrollTop = LogArea.scrollHeight
   
Else
       
WolSuccess = 1
       
LogArea.Value = LogArea.Value & "Wakeup sent to " & strMACAddress & "\" & strIPAddress & vbCrLf
       
LogArea.scrollTop = LogArea.scrollHeight
       
LogFile.WriteLine(Date & " " & Time & vbTab & arrComputers(iList) & ": Wakeup sent to " & strMACAddress)
   
End If
Next

No i wtedy działa :) Jeszcze jedna uwaga – w czasie szukania pól do skryptów niezastąpione jest narzędzie WMI CIM Studio. tylko należy pamiętać, żeby wybrać do przeglądania namespace \\Srv1\root\SMS\site_LAB gdzie Srv1 to nazwa serwera SCCM, a LAB to trzyliterowy kod site’u. Najciekawsze klasy są w gałęzi – SMS_Group\SMS_G_System_Current\*

wtorek, 4 sierpnia, 2009 - 14:15

Powershell portable

Autor: domel | Kategorie: ideas, script/developer, server, tools

Powstaje przenośna wersja PS :D na razie w fazie closed beta. nie ukrywam, że fajnie by było mieć możliwość korzystania z PowłokiMocy w śrowisku WinPE lub BartPE :) Więcej szczegółów tutaj: http://karlprosser.com/coder/2009/07/21/shelltools-portable-powershell-description-survey-and-private-beta/

czwartek, 9 lipca, 2009 - 9:22

Visual Studio Unattended

Przyszły wakacje, a więc czas odświeżania oprogramowania w salach laboratoryjnych w PJWSTK. Oczywiście wszystko powinno być nienadzorowane itd. Przez lata instalacje WinXP leżące na RIS było modyfikowane wielokrotnie – Peki, który jest autorem systemu teraz by go już nie poznał ;)

Dobra – zadnie polega na nienadzorowanej instalacji MSSQL2008 (tylko narzędzia klienckie) i Visual Studio 2008. Pierwszą rzeczą jest ustalenie kolejności instalacji. Gdyby zaczynać on VS, to trzeba by zrobić tak:

  • .NET 3.5 + Windows Installer 4.5 (wymaganie SQL2008)
  • restart
  • VS2008
  • restart
  • SP1 do VS2008
  • restart
  • SQL2008
  • restart

No i jakoś dużo tych restartów :/ gdyby zrobić odwrotnie? Zazwyczaj staram się instalować oprogramowanie według kolejności w jakiej wychodzi na świat, ale tutaj zrobię na odwrót :)

  • .NET 3.5 + Windows Installer 4.5
  • restart
  • SQL2008
  • restart
  • VS2008
  • teoretycznie restart, ale już nie wymagany :)

Jest krócej i to sporo :) Dzielimy to na 3 skrypty vbs. stage1.vbs, stage2.vbs i stage3.vbs 
Pierwszy wygląda tak:

Dim WshShell   
Set WshShell = WScript.CreateObject("WScript.Shell")   
WshShell.run "C:\scripts\frmwrk\dotnetfx35.exe /qb /norestart",1,true ‘.NET 3.5   
Wait "msiexec.exe","Administrator"   
Wait "setup.exe","Administrator"   
WshShell.run "C:\scripts\frmwrk\dotnetfx35SP1.exe /qb /norestart",1,true ‘.NET 3.5 SP1   
Wait "msiexec.exe","Administrator"   
Wait "setup.exe","Administrator"   
‘Windows Installer 4.5   
WshShell.run "C:\scripts\WindowsInstaller\WindowsXP-KB942288-v3-x86.exe /passive /norestart /overwriteoem /nobackup",1,true   
Wait "msiexec.exe","Administrator"   
Wait "setup.exe","Administrator"   
‘autologin do stage 2   
WshShell.run "c:\Windows\regedit.exe /s c:\scripts\stage2.reg",1,true   
WshShell.run "c:\scripts\shutdown.exe -r -t 30 -f",1,false   

Sub Wait (processName, userName)    
 
On Error Resume Next    
 
Dim objProcess   
 
Dim wmi   
 
Dim isRunning   
 
Dim strUser   
 
Dim return   
 
isRunning = True   
 
set wmi=GetObject("winmgmts:")   
     
 
Do While isRunning   
   
isRunning = False   
   
for Each objProcess in wmi.ExecQuery("select * from Win32_Process")   
       
strUser = ""   
       
return = objProcess.GetOwner(strUser)   
       
If return = 0 Then   
           
if UCase(objProcess.name) = UCase(processName) And UCase(strUser) = UCase(userName) then   
               
isRunning = True   
           
End if           
       
End If   
   
Next   
   
wscript.sleep 1000   
 
Loop   
wscript.sleep 10000   
     
End Sub  

Dla Ciekawskich: Plik stage2.reg wygląda tak:

Windows Registry Editor Version 5.00 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]  
"DefaultDomainName"
="PJWSTK"  
"DefaultUserName"
="Administrator"  
"DefaultPassword"
="StandardoweHasloAdmina"  
"AutoAdminLogon"
="3"  
"ForceAutoLogon"
="1"  

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]  
"afterreboot"
="c:\\scripts\\stage2.vbs"

Jedziemy dalej. Żeby zrobić nienadzorowaną instalację SQL 2008 trzeba przygotować response file. Jako, że informatycy są z natury ludźmi leniwymi pozwoliłem, żeby setup MSSQL zrobił go za mnie :) Po wyklikaniu jakie składniki chcemy mieć na komputerze (w procesie zwykłej instalacji – więcej szczegółów tutaj). Jak już mamy plik odpowiedzi, to jedziemy.

stage2.vbs:

Dim WshShell  
Set WshShell = WScript.CreateObject("WScript.Shell")  
‘naprawiamy MSDTC - czasami przez zepsuty log instalacja potrafi sie wywalać. polega to na zatrzymaniu usługi MSDTC, wywołaniu msdtc -resetlog i ponownym uruchomieniu MSDTC  
WshShell.run "c:\scripts\fixmsdtc.cmd",0,true  
‘zatrzymanie usługi Automatic Updates - nie chcemy, żeby jakiś inny setup chodził w tle i nam coś popsuł :)  
WshShell.run "c:\scripts\updatesstop.cmd",1,true  
WshShell.run "P:\Db\Microsoft\MsSQL2008dev\setup.exe /q /CONFIGURATIONFILE=P:\Db\Microsoft\MsSQL2008dev\ConfigurationFile.ini",0,true  
Wait "setup.exe","Administrator"  
‘znów MSDTC - same kłopoty z tą usługą :/  
WshShell.run "c:\scripts\fixmsdtc.cmd",0,true  
‘autologin do stage 3  
WshShell.run "c:\Windows\regedit.exe /s c:\scripts\stage3.reg",1,true  
WshShell.run "c:\scripts\shutdown.exe -r -t 30 -f",1,false  

Sub Wait (processName, userName)   
 
On Error Resume Next   
 
Dim objProcess  
 
Dim wmi  
 
Dim isRunning  
 
Dim strUser  
 
Dim return  
 
isRunning = True  
 
set wmi=GetObject("winmgmts:")  
    
 
Do While isRunning  
   
isRunning = False  
   
for Each objProcess in wmi.ExecQuery("select * from Win32_Process")  
       
strUser = ""  
       
return = objProcess.GetOwner(strUser)  
       
If return = 0 Then  
           
if UCase(objProcess.name) = UCase(processName) And UCase(strUser) = UCase(userName) then  
               
isRunning = True  
           
End if          
       
End If  
   
Next  
   
wscript.sleep 1000  
 
Loop  
 
wscript.sleep 10000    
End Sub 

No już prawie koniec – jeszcze tylko Visual Studio. I tu zaczynają się schody. Teoretycznie robię to według tej instrukcji http://www.msfn.org/board/index.php?showtopic=117875, ale coś się nie udawało. Troszkę poczytać i wiem, że mój system to WinXP SP3. Ta aktualizacja zawiera już w sobie Windows Installera 3.5, więc trzeba odpowiednie wpisy usunąć z pliczku ini (więcej informacji tutaj). Ale to niestety jeszcze nie wszystko. Próba ciche instalacji się kończy po paru minutach. Wystarczy spojrzeć do Event Viewer-a, żeby zobaczyć, że Windows Installer próbuje zainstalować .NET 3.5. Jako, że już mamy nowszą wersję, to  setup zwraca błąd i sie kończy. Więc znów trzeba grzebać się w pliczku. Usuwamy wpisy dotyczące net framework z [PreInstallOrder], [InstallOrder] i [PostInstallOrder] oraz zmieniamy InstallActionInteger komponentu na 1. Teraz bangladesz :) dzięki temu mamy taki pliczek stage3.vbs:

Dim WshShell  
Set WshShell = WScript.CreateObject("WScript.Shell")  
WshShell.run "P:\Instalki\MICROSOFT\Developer\visual_studio_2008\Setup\setup.exe /UnattendFile P:\Instalki\MICROSOFT\Developer\visual_studio_2008\2008v3.ini",0,true  
Wait "setup.exe","Administrator"  
WshShell.run "cmd /c""echo y|gpupdate /force """,1,true  
WshShell.run "c:\winnt\shutdown.exe -r -t 30",1,false  

Sub Wait (processName, userName)   
 
On Error Resume Next   
 
Dim objProcess  
 
Dim wmi  
 
Dim isRunning  
 
Dim strUser  
 
Dim return  
 
isRunning = True  
 
set wmi=GetObject("winmgmts:")  
    
 
Do While isRunning  
   
isRunning = False  
   
for Each objProcess in wmi.ExecQuery("select * from Win32_Process")  
       
strUser = ""  
       
return = objProcess.GetOwner(strUser)  
       
If return = 0 Then  
           
if UCase(objProcess.name) = UCase(processName) And UCase(strUser) = UCase(userName) then  
               
isRunning = True  
           
End if          
       
End If  
   
Next  
   
wscript.sleep 1000  
 
Loop  
 
wscript.sleep 10000    
End Sub

To by było na tyle :) Działa aż miło :D

niedziela, 28 czerwca, 2009 - 14:10

skrypt mapujący dyski sieciowe

Autor: nExoR | Kategorie: script/developer

prosty problem: należy spisać co userzy mają podmapowane oraz jakie mają drukarki sieciowe.
sposób 1: podchodzi i spisywać [BUEEEE!]
sposób 2: napisać skrypt. nie jest to trudne i szybko można znaleźć gotowca w netcie ale jeden drobny problem – zarówno ‘net use’ jak obiekt ‘wscript.network’ nie wyświetlają dysqw, do których nie podano haseł. tymczasem na liście w exploratorze normalnie je widać. po kliknięciu na dysk pojawia się prośba o hasło i dopiero user może skorzystać z dysq. aby wylistować takie jeszcze-niezmapowane dyski wystarczy odczytać je z klucza HKCU\Network:


const HKCU=&H80000001
Set WshNetwork = WScript.CreateObject(”WScript.Network”)
computername=WshNetwork.ComputerName
set fso=createObject(”Scripting.FileSystemObject”)
set f=fso.CreateTextFile(computername&”.log”)
f.writeline wshnetwork.username
f.writeLine “DYSKI SIECIOWE:”
‘enum by network
‘ Set AllDrives = WshNetwork.EnumNetworkDrives()
‘ If AllDrives.Count = 0 Then f.writeLine “No Network Disk Mapped “
‘ For i = 0 To AllDrives.Count - 1 Step 2
‘ f.writeLine AllDrives.item(i)&” “&AllDrives.item(i+1)
‘ Next
‘enum by registry
set oReg=getObject(”winmgmts:!\\.\root\Default:StdRegProv”)
oReg.enumKey HKCU,”Network”,subKeys
for each skey in subKeys
oReg.getStringValue HKCU,”Network\”&skey,”RemotePath”,val
f.writeLine skey&”: “&val
next
f.writeLine vbcrlf&”DRUKARKI SIECIOWE:”
Set objPrinter = wshNetwork.EnumPrinterConnections()
‘ Extra section to troubleshoot
If objPrinter.Count = 0 Then f.writeline “No Printers Mapped “
‘ Here is the where the script reads the array
For intDrive = 0 To (objPrinter.Count -1) Step 2
intNetLetter = IntNetLetter +1
f.writeLine objPrinter.Item(intDrive) & ” = ” & objPrinter.Item(intDrive +1) & ” Printer : ” & intDrive
Next
f.writeLine “done.”

n.