Tag-arkiv: Powershell

Gennemsøgning af tekst-filer

Fik en forespørgsel idag om jeg kendte et værktøj der kunne gennemse en masse tekstfiler for en bestemt streng og det gør jeg da. Det er nemlig lige et job for select-string.

Meget kort kunne det jo gøres vha:

Select-String -Path “sti til tekstfilerne” -Pattern “den tekst der skal findes”

Men output er ikke ligefrem lækkert og noget du kan servere for en chef eksempelvis ;) Så jeg brugte Out-Gridview så de kunne gennemgå om de havde fundet det forventede og derefter smider vi det ud i en komma-separeret fil, således:

Select-String -Path “sti til tekstfilerne” -Pattern “den tekst der skal findes” | Select Filename,Path | Outgrid-view -Passthru | Export-csv “sti til csv filen du vil lave” -NoTypeInformation -Delimiter “;” -Encoding “BigEndianUnicode”

Det var en hurtig og nem operation. ;)

 

Billeder i Active Directory

Jeg har i et stykke tid benyttet CodeTwo´s program “Active Directory Photos” til at opdatere brugernes billeder i Active Directory, det er nemlig et fantastisk produkt, hvis man bare skal have opdateret en sjælden gang. Find det her. Det er dog rimeligt besværligt at benytte hvis man skal opdatere dagligt eller ugentligt fra en base af billeder. Jeg har derfor lavet nedenstående script der kan gøre det helt automatisk..

Billeder i Active Directory lægges i den attribut der hedder ThumbnailPhoto og for at lægge et billede i den skal vi først konvertere billedet til en struktur som powershell forstår. Og da der er mere end 1 billede lægger jeg dem selvfølgelig i en variabel form.

Billedet hentes ind i en variabel

$Photo = [Byte[]] (Get-Content “sti til billeder” -Encoding Byte)

Og dette lægges ind i attributten ThumbnailPhoto ved at gøre følgende:

Set-ADUser -Identity “Username” -Replace @{thumbnailPhoto=$photo}

Ovenstående er grundfunktionerne og for at gøre det for flere billeder skal vi selvfølgelig bruge lidt mere avancerede funktioner. I følgende eksempel kører jeg en 3-trins raket af et script, først hentes brugernavne i AD og dernæst hentes billeder på en sharepoint side (da det typisk er der man vil have et billede af de ansatte) og til sidst uploader vi billederne til AD. Bemærk at jeg ikke har angivet nogle bruger med rettigheder til at hente brugernavne fra AD, jeg forventer at dette er muligt for alle. Men hvis dit Active Directory har enablet ListObjectModeOnly og ikke tillader dette, skal du selvfølgelig også angive en bruger til at hente brugernavne med.

$Cred = Get-credential  – brugernavn og kode der giver rettigheder til at hente billederne

$Cred2 = Get-Credential - Brugernavn og kode der giver rettigheder til at uploade billederne

$A = (Get-ADUser -filter * | where {$_.samaccountname.length -eq 5}).samaccountname - Henter brugernes samaccountname

foreach ($item in $A)

{Invoke-WebRequest -Uri “hjemmeside url/$item.jpg” -Method “get” -OutFile folder\$item.jpg -Credential $cred} - Henter billederne

foreach ($item in $A)

{$photo = [byte[]](Get-Content folder\$item.jpg -Encoding byte) - Konverterer dem til byte

Set-ADUser $item -Credential $Cred2 -Replace @{thumbnailPhoto=$photo} } - Uploader billederne

Ovenstående vil selvfølgelig fejle hvis ikke der er et billede til brugeren på siden og det kræver at billederne er navngivet brugernavn.jpg og er måske ikke brugbart for alle, men det giver et meget godt indblik i hvad der skal til for at uploade billeder automatisk. Det er også muligt at man kan finde en bruger der har adgang til det hele og så er det selvfølgelig kun 1 $cred der skal bruges.

 

Backup af GPO

At lave en backup af alle gruppe politikker i et domæne, kan man selvfølgelig bruge den indbyggede backup i den grafiske brugerflade (Group Policy Management Console), men det er jo noget sejere at lave sin helt egen backup gennem powershell. :)

Først skal vi finde en lokation hvor vi vil gemme vores backup og dernæst skal vi lave et lille script der foretager backuppen.

Start med at importere group policy modulet

Import-Module GroupPolicy

Dernæst skal vi have lavet et bibliotek der indeholder dagens backup (det kan selvfølgelig også være månedens backup, det er helt op til dig selv.) Bemærk at måned er med stort M, hvis du bruger et lille m er det minut du beder den om at sætte ind, de 4 yyyy gør at året bliver 2013 istedet for yy 13.

$Lib = Get-Date -Format d.M.yyyy

Så skal vi lave et bibliotek der hedder dags dato.

New-Item -Path lokation\$Lib -Itemtype directory

Og så skal vi køre en backup af alle politikkerne.

Backup-GPO -All -Path lokation\$Lib

Det var det hele. man kan så efterfølgende lave et script der trækker et antal dage fra dags dato og sletter de gamle biblioteker.

Det endelige script ser sådan ud:

$Lib = Get-Date -Format d.M.yyyy

$Lib = Get-Date -Format d.M.yyyy

Backup-GPO -All -Path lokation\$Lib

Så skal det bare signeres og scheduleres og så er den ged barberet.. ;)

Active Directory backup

Backup af Active Directory er ganske simpelt på windows 2008 server og senere versioner, det svære er at restore objekter enkeltvis. Jeg har set flere der betaler for backup programmer der kan dette, men gætter på at det skyldes historiske årsager at de ikke er skiftet ud for længst!

Men lad os lave en backup af vores AD først så kan vi kigge på restore af det bagefter.

For at tage backup af AD skal man først installere Windows Backup. Dette gøres nemt ved at køre add-windowsfeature windows-server-backup fra en powershell prompt, jeg plejer at give den en -IncludeAllSubFeature også så jeg er sikker på at der ikke mangler en eller anden komponent. Det kan være utrolig tidskrævende at fejlfinde på den slags.. ;)

Add-WindowsFeature Backup-Features -IncludeAllSubFeature

-Name er en optional og kan derfor undværes og hvis man gør det fra powershell 2.0 skal man først loade modulet servermanager, det sker automatisk i 3.0.

Når Windows Backup er installeret kan man køre backuppen fra en prompt

WBAdmin Start SystemStateBackup -BackupTarget:D: -Quiet

Ovenstående kan scheduleres til at køre som man lyster, jeg kører den dagligt kl 21. Scheduleringen kan laves ved at køre følgende:

schtasks /create /SC DAILY /ST 21:00 /RU SYSTEM /TN BackupSystemstate /TR “wbadmin start systemstatebackup -backupTarget:C: -quiet -output:D:\WindowsImageBackup\Backup.txt”

Nedenstående sørger for at vi kun har 5 versioner efter hver backup er kørt. Den sletter alt andet end de sidste 4 og da backuppen så kører 15 min senere har vi 5 versioner. Det er selvfølgelig ikke nok for de fleste virksomheder, da brugere ofte opdager problemer senere end 5 dage og derfor kan man selvfølgelig rydde op som man lyster. Jeg har gjort det på denne måde da jeg så har 5 versioner online og vores backup system kommer så senere og tager en backup af disken og gemmer dem i 5 år. Så er jeg rimelig sikker på at kunne behage alle der måtte have lyst til at kigge i AD historik eller få deres bruger/grupper osv tilbage.

schtasks /create /SC DAILY /ST 20:45 /RU SYSTEM /TN BackupScavenge /TR “wbadmin delete systemstatebackup -keepversions:4 -quiet -output:D:\WindowsImageBackup\BackupScavenge.txt”

Såfremt backuppen der tages skal skrives til samme disk som Active Directory er installeret på er der lige en lille krølle. Windows Backup kan ikke tage backup til samme disk som den tager backup af, dette giver mening for en fuld disk-backup, men for systemstate burde det være ok at lægge den der midlertidigt. Derfor har MS også været så venlige at muliggøre dette ved en lille ændring i Registry. Windows Backup ligger under HKLM\System\CurrentControlSet\Services\WBEngine og hvis ikke den er der allerede, skal man oprette en nøgle der hedder SystemStateBackup og tilføje en 32-bit Dword værdi, navngive den AllowSSBToAnyVolume (Allow System State Backup To Any Volume) og give den værdien 1.

New-Item -Path HKLM:\SYSTEM\CurrentControlSet\Services\wbengine -Name SystemStateBackup

New-ItemProperty “HKLM:\SYSTEM\CurrentControlSet\Services\wbengine\SystemStateBackup” -Name “AllowSSBToAnyVolume” -Value 1 -PropertyType “DWord”

Eller hvis du vil være old-school

reg add HKLM\SYSTEM\CurrentControlset\Services\wbengine\SystemStateBackup /v AllowSSBToAnyVolume /t REG_DWORD /d 1 /f

Vend wbengine servicen og så er det muligt at smide en midlertidig backup af AD på samme drev som AD har sine database.

Nu har vi en backup og så håber vi selvfølgelig at vi aldrig kommer til at bruge den. Men hvis vi skal så kig på restore-artiklen for en nem vejledning i restore af objekter i AD.

Signering af scripts

Jeg har flere scripts der kører automatisk i løbet af natten. For at disse kan køre har jeg signeret dem med et kode signerings certifikat. Processen er egentlig ganske simpel, forudsat at du har adgang til sådan et certifikat. Da jeg kører mine scripts i mit eget domæne, er det nemmeste i verden jo at installere en CA og udstede sit eget. (Installation af CA og udstedelse af certifikat, tager vi lige en anden dag. ;) )

Men når du har certifikatet skal du bruge kommandoen Set-AuthenticodeSignature, pege på dit script og dit cert. Kommandoen ser sådan ud:

Set-AuthenticodeSignature “sti til script” -Certificate “sti til certifikat”

Nemt ikke? Lad os forestille os at jeg har lagt scriptet script.ps1 i temp folderen på mit c-drev og jeg kun har 1 certifikat til kode signering, så ser kommandoen således ud:

Set-AuthenticodeSignature C:\Temp\Script.ps1 -Certificate (Get-Childitem Cert:\CurrentUser\My -CodesigningCert)

Og da -Certificate er en positionsbestemt parameter, så er det ikke engang nødvendigt at angive den. Og kommandoen ovenfor kan forkortes til:

Set-AuthenticodeSignature C:\Temp\Script.ps1 (Get-Childitem Cert:\CurrentUser\My -CodesigningCert)

Positionsbestemte parametre er det Powershell forventer der kommer på den position, altså skal certifikatet angives som parameter nr 2 og man kan undlade at angive dette og bare sætte certifikatet ind her.

Igen tror jeg Voila er ordet. ;)

Domæne overblik fra powershell

Domæne class´en er ligesom forest class´en, dog indeholder den en masse information om domænet.. (Big surprise)

I domæne class´en finder man ejeren af domæne rollerne (og vi ved jo alle at det er PDC emulator, RID master og Infrastructure master), domæne controllere, domænets version (mode) og en masse andet. Igen kan vi trække disse informationer ud og modulere dem på kryds og tværs.

Vi skal have fat i class´en først og jeg vil som altid gerne undgå at skrive alt for meget, derfor smider jeg den i en variabel.

 

$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::getcurrentdomain()

For så at få domæne controllere ud af denne liste, kan man skrive:

$Domain.get_domaincontrollers() | fl

Ydermere hvis man ønsker at få både en liste over domæne controllerne og deres operativ system kan man skrive:

$Domain.get_domaincontrollers() | fl name,OSVersion

Så er det trods alt nemmere at dokumentere sit AD. ;)

AD Sites fra powershell

Mangler du et hurtigt overblik over dine AD Sites? Jeg synes ofte jeg roder rundt i Sites and Services og leder. Og man kan sige meget om Sites and Services, men hvis man har 100+ sites så bliver det uoverskueligt. Med powershell er det muligt at trække information om dine sites ud sortere, filtrere og exportere data på kryds og tværs som det nu lyster dig.

For at trække data fra AD omkring dine sites skal vi have fat i forest class. Dette gør jeg normalt ved at lægge den i en variabel, på den måde slipper jeg for at skrive en meget lang class streng igen og igen.

$Forest = [System.DirectoryServices.ActiveDirectory.Forest]::getcurrentforest()

Nu indeholder variablen $Forest vores forest class. For at hente vores sites fra den skrives:

$Forest.get_sites() | fl

Forest class´en indeholder meget mere info, prøv bare at skrive $Forest, frem kommer navn, sites, domæner, gc, forest mode, rod domæne osv.

Oprettelse af AD bruger fra Powershell

Nu er oprettelse af en bruger fra powershell ikke ligefrem hjernekirurgi, men der er alligevel nogle detaljer der kan besværliggøre det.

Oprettelse af en bruger er simpelt: New-Aduser “Michael Hovej”

Problemet med min nye bruger er at han ikke kan logge på, men det løser man vel ved at skrive: New-ADUser “Michael Hovej” -PassThru | Enable-ADAccount ikke?

Desværre er det ikke så simpelt. Min nye bruger har ikke fået defineret et kodeord og da AD ikke vil aktivere brugere der ikke har et kodeord, skal det sættes først. Men mon ikke det så bare er at skrive New-ADUser “Michael Hovej” -AccountPassword “Password01″ -PassThru | Enable-ADAccount? Igen nej. Powershell smider ikke et kodeord videre til AD i klar tekst. Kodeord skal skrives som såkaldte secure-strings. Der er 2 måder. Den ene måde er at samle det op i en variabel som vist her:       $Cred =  Read-Host “Indtast Password” -AsSecureString og så bruge variablen til at sætte kodeord med, som her: New-ADUser “Michael Hovej” -AccountPassword $Cred -Passthru | Enable-ADAccount Nu er brugeren oprettet og aktiveret.

Der er som sagt 2 måder og den anden muliggør at vi laver brugeren, sætter kodeord og aktiverer brugeren i en “one-liner” og det er jo så sexet som det kan blive. ;)

New-ADUser “Michael Hovej” -AccountPassword (ConvertTo-SecureString “Password01″ -AsPlainText -Force) -PassThru | Enable-ADAccount

Bemærk parameteren -PassThru som sender det objekt der lige er lavet videre til næste kommando, meget smart. Skal ovenstående bruges sammen med en csv import skal man bare køre den med en for-each som her:

Import-Csv “sti til csv fil” | ForEach-Object {New-ADUser $_.Name -AccountPassword (ConvertTo-SecureString “Password01″ -AsPlainText -Force) -PassThru | Enable-ADAccount

$_.Name er så “Michael Hovej” i csv filen og man kan selvfølgelig smide alle de variabler ind fra csv´en som det er muligt og så oprette en bunke brugere.

Voila er vist ordet. ;)

Sammenligning af tekstfiler

Jeg trækker ofte større lister ud af forskellige systemer og skal sammenligne listerne. Sålænge det er sammenlignelige data, eks brugernavne, burde det vel være en simpel sag..? Ja, sålænge det drejer sig om et lille antal. Men jeg sammenligner ofte tusindvis af eks brugernavne. Til det benytter jeg compare-object i Powershell.
Først skal man sikre sig at ens data grundlag er ens, altså har man 2 komma-separerede dokumenter der indeholder brugernavne, skal de enten KUN indeholde brugernavne eller også skal man kopiere brugernavnene over i en fil, så de er for sig selv. Jeg laver ofte 2 txt filer, lad os kalde dem compare1 og compare2. For at disse kan sammenlignes skal man hente indholdet af dem over i en variabel. Sammenligner man dem ved at skrive filnavnene som compare objekter, fortæller Compare-Object dig at filerne ikke er ens og det er jo i grunden korrekt. Men da det er indholdet vi vil have sammenlignet henter vi dem altså først ind i en variabel ved at skrive:

$Comparefile1 = Get-Content “sti”\compare1.txt

og ligeså for næste fil

$Comparefile1 = Get-Content “sti”\compare2.txt

Nu har vi altså 2 variabler der indeholder vores brugernavne (hvis det er det der er i filerne) og disse sammenligner man så ved at skrive:

Compare-Object $Comparefile1 $Comparefile2

Outputtet ligner dette:
User1 <=
User2 =>
Hvilket betyder at User1 er i comparefile1, User2 er i comparefile2.

Ønsker man også at have de brugere der er ens med, skal man bruge parameteren -includeequal og så bliver kommandoen:

Compare-Object $Comparefile1 $Comparefile2 -IncludeEqual

Outputtet ligner så dette:
User1 <=
User2 =>
User3==
Hvilket betyder at User1 er i comparefile1, User2 er i comparefile2 og User3 er i begge filer.

Ovenstående giver output til skærm og det er måske godt nok, men hvis det skal bruges efterfølgende kan man selvfølgelig også sende det til en komma-separeret fil ved at skrive følgende:

Compare-Object $Comparefile1 $Comparefile2 -IncludeEqual | Export-Csv C:\temp\csvfil.csv