Stop steroïden in je PoSH ISE

Door ralpje op maandag 27 oktober 2014 22:15 - Reacties (7)
Categorieën: ISESteroids, Powershell, Views: 3.726

Ik maak in m'n dagelijks werk veel gebruik van Powershell. Of het nu is voor het beheren van Office365, Exchange of ActiveDirectory of het genereren van overzichten voor managementinformatie, PoSH is de eerste (en meestal ook de enige) tool waar ik op teruggrijp.
Standaard wordt in Windows naast PoSH ook de Integrated Sript Editor (ISE) geïnstalleerd, waarmee PoSH-scripts kunnen worden gemaakt en getest. Die ISE mist echter wel de uitgebreidere functionaliteit dat je gewend bent van 'echte' scripteditors zoals Visual Studio. Daar is echter een oplossing voor: ISESteroids.
ISESteroids gebruik ik al sinds versie 1. De versie 2 die nu in ontwikkeling is geeft echter heel veel redenen om er een blogpost aan te wijden!

Misschien wel het grootste USP van ISESteroids is de prijs. Voor de 'professional' license betaal je ¤99. De mooiste functies zitten echter in de 'enterprise' uitvoering, die voor ¤189 aangeschaft kan worden. Wie nu echter de huidige 1.x-versie aanschaft voor ¤99, krijgt bij het final worden van 2.0 de upgrade naar enterprise gratis. Voor Hollanders natuurlijk een uitstekende deal ;)

Het installeren van ISESteroids is eenvoudig: download de zip-file en kopieer de inhoud naar één van de modules folders. Geen idee wat je modules-folders zijn? PoSH to the rescue.

code:
1
$env:PSModulePath -split ';'


Na het kopieren van de files kun je ISESteroids in je PoSH ISE starten:

code:
1
start-steriods


Uiteraard is het handig als dit standaard gebeurt bij het starten van ISE. Handig: in ISE Steroids kun je eenvoudig één van de profile-scripts openen: voor de console, voor ISE of voor beiden. Uiteraard wordt er ook onderscheid gemaakt tussen het profile van de huidige gebruiker, of die voor alle gebruikers. Door het start-steroids commando aan de 'all users (ISE)' profile toe te voegen, wordt ISESteroids nu automatisch gestart bij het openen van ISE.
ISESteroids profile scripts

De opvallendste wijziging zijn de extra toolbars die beschikbaar komen.
ISESteroids toolbar
Al die extra knoppen bieden uiteraard extra functies. Een deel daarvan zet ik hier even snel op een rijtje, de komende tijd zal ik bepaalde functies apart in een blogpost toelichten.

Eén van de dingen waar ik zelf erg blij mee ben is de versioning. Je kunt het versioning scherm activeren via de toolbar-knop met het klokje. Als versioning actie is verschijnt een extra menu aan de rechterkant van het scherm, waarin de verschillende versies van het script waarin je werkt getoond worden.
ISESteroids versioning
In auto-mode wordt bij het opslaan van je script direct een nieuwe 'minor' versie gemaakt, je kunt via 'add new version' zelf een major version opslaan. Uiteraard is het mogelijk om notities aan een bepaalde versie toe te voegen of via een compare snel een overzicht te krijgen van de wijzigingen sinds een bepaalde versie.
De verschillende versies worden opgeslagen in een .zip file op de locatie van het script. Als dit dus op een netwerkdrive is kun je op deze manier eenvoudig met verschillende mensen aan hetzelfde script werken en ondertussen alle wijzigingen bijhouden.

Wellicht de meest uitgebreide functie in ISESteroids is de 'refactor script' knop. Met deze knop wordt je script in één klap bijna volledig herschreven en vooral leesbaar gemaakt voor anderen: overbodig spaties worden verwijderd, line endings worden genormaliseerd, er wordt case-correct gedaan voor commands en paramaters, script blocks worden automatisch ingesprongen, etc. Via de 'advanced' tab bepaal je zelf wat er gebeurd, op de 'basic' tab kun je alle acties die je ingeschakeld hebt direct allemaal starten voor je script.
ISESteroids refactoring

Mensen die veel in grotere stukken code werken zullen veel baat hebben bij de ScriptMap. Dit is een visuele weergave van je plek in het script; het geeft aan de rechterzijde met een groene highlight aan waar in je code je momenteel beeld. Een mouse-over geeft een ingezoomde weergave van dat stuk code zodat je makkelijk kunt navigeren.
ISESteriods scriptmap

Intelliselect is een functie die in de praktijk ook erg nuttig blijkt, bijvoorbeeld om snel een bepaalde function uit te commenten.
Als de cursor zich in de code bevindt wordt met de toetscombo cntrl+Q het actuele element geselecteerd.
ISESteroids IntelliSelect
Door nogmaals op cntrl+Q te drukken wordt het bovenliggende element geselecteerd... Enzovoort.
ISESteroids IntelliSelect
Als je eenmaal het gewenste codeblock geselecteerd hebt, kun je cntrl+shit+b de selectie uitcommenten.
ISESteroids comment

Heb je de behoefte om de naam van een variabele in je hele script aan te passen? Plaats je cursor ergens in je script in de variabele en druk op F2. Hiermee wordt de variable en alle voorkomende instances ervan geselecteerd.
ISESteroids IntelliSelect
Door nu simpelweg de nieuwe naam voor te typen wordt deze overal in het script aangepast.
ISESteroids IntelliSelect

In bovenstaande screenshots zie je dat voor de functions in het script wordt aangeven hoe vaak de betreffende function gebruikt wordt. Door te klikken op deze waarde wordt direct naar deze 'references' genavigeerd.
ISESteroids references
Zijn er meerdere references, dan worden deze in een lijst weergeven met de bijbehorende code-regel en kan vanaf daar doorgeklikt worden naar de betreffende regel.
ISESteroids reference list

Dit zijn nog maar een paar voorbeelden van de praktische zaken die ISESteroids aan je PoSH ISE toevoegd. In komende blogposts zal ik een aantal functies nog extra onder de loep nemen!

Exchange-taken automatiseren met cmdlet extenstion agents

Door ralpje op maandag 7 oktober 2013 09:30 - Reacties (2)
Categorieën: Exchange, Powershell, Scripting, Windows, Views: 3.883

Er zijn dagen dat ik medelijden heb met mijn collega's. Niet omdat het werk vervelend is of omdat de koffie op kantoor slecht is, maar omdat ze mij als collega hebben. En dan met name omdat ik bij elke vraag om assistentie vraag: "heb je powershell al geopend?" ;)
In Server 2012 en Exchange 2013 blijkt eens te meer dat Powershell the way to go is. De grafische beheersmogelijkheden worden steeds verder uitgekleed en zijn, zeker in Exchange, al een tijdje niet meer dan een grafische schil om Powershell heen.
Toch zijn er zaken die in zowel Powershell als de EMC niet direct voor zich spreken. Eén van die dingen kwam ik afgelopen week weer tegen: Exchange retention policies en hoe deze op nieuwe mailboxen toegepast kunnen worden.

Een klant gebruikt Exchange server (in dit geval Exchange 2010 SP3) in combinatie met een softwarebased anti-spam pakket. Van oudsher is de organisatie gewend aan een 'spam'- of 'junk'mail folder in Outlook, waar de anti-spam software de verdachte mails in neerzet. De gebruiker kan vervolgens zelf beoordelen of de mail inderdaad spam is, of dat er een legitieme email per abuis als spam gemarkeerd is. Er is één nadeel: mensen checken de folder, lezen of verplaatsen de mail die ze nodig hebben en negeren de rest. De spamfolder wordt daarmee een vergaarbak van slechte emails die onnodig storage en backup consumeren maar natuurlijk eigenlijk gewoon verwijderd moeten worden.

De oplossing is relatief simpel: retention policies. Maak een retention tag voor de 'junk'folder, stel hem in op 'delete after 30 days' en maak vervolgens een retention policy die je op alle mailboxen kunt gaan toepassen.

Het daadwerkelijk toepassen op de mailboxen is in Powershell zo gepiept:

PowerShell:
1
get-mailbox -resultsize unlimited | foreach {set-mailbox $_.identity -retentionpolicy policy}



Zo simpel is het.... Maar de policy is nu alleen van toepassing op de bestaande mailboxen. Mailboxen die nieuw worden aangemaakt krijgen niet automatisch deze policy mee. Ook is het niet mogelijk om een default policy voor je Exchange-omgeving of mailboxdatabase in te stellen.
Ongeacht of je de EMC of Powershell gebruikt zal je dus bij het aanmaken van een mailbox expliciet moeten aangeven dat je deze policy wilt gebruiken.

En dát is dus waar de exchange cmdlet extension agents om de hoek komen kijken; in dit geval de scripting agent.
Exchange heeft een aantal 'extension agents', bijvoorbeeld voor management van het Offline Address Book. De scripting agent is één van deze in totaal zeven agents en is de enige die standaard nog niet is ingeschakeld.

De werking van deze agent is redelijk eenvoudig, maar toch is het vaak een onbekend of, wellicht beter, onderbelicht deel van Exchange. Na het activeren van de scripting agent wordt die agent elke keer aangeroepen als er een een cmdlet op de server uitgevoerd wordt. Dat gebeurt niet alleen bij cmdlets die direct van Powershell worden uitgevoerd, maar ook als ze worden uitgevoerd door de Exchange Management Console, Exchange services of Exchange Control Panel.
Als de agent wordt aangeroepen wordt er gecontroleerd of er scripts geconfigureerd zijn voor dat betreffende cmdlet. Dat is dus wat we gaan doen om te zorgen dat onze retention policy netjes op alle nieuwe mailboxen wordt toegepast.

Als eerste moeten we zorgen dat de scripting agent geactiveerd wordt. Dit is redelijk eenvoudig: in de <installation path>\V14\Bin\CmdletExtensionAgents folder staat een ScriptingAgentConfig.xml.sample. Door deze te hernoemen naar ScriptingAgentConfig.xml hebben we een config file voor de scripting agent. De agent zelf activeren we vervolgens vanuit Powershell:

PowerShell:
1
Enable-CmdletExtensionAgent "Scripting Agent"



Nu nog zorgen dat we het script laten doen wat we willen :) Laten we eerst naar het volledige script kijken, daarna breken we het in stukken om te zien wat het doet:

XML:
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8" ?>
<Configuration version="1.0">
<Feature Name="Mailboxes" Cmdlets="New-Mailbox,Enable-Mailbox">
<ApiCall Name="OnComplete">
if($succeeded) {
$Name= $provisioningHandler.UserSpecifiedParameters["Alias"]
Set-Mailbox $Name -RetentionPolicy "Retention Policy"
}
</ApiCall>
</Feature>
</Configuration>



Het eerste dat opvalt is dat het eigenlijk geen script maar XML is. In de XML-file wordt gespecificeerd welke script in welke situatie moet worden uitgevoerd. Door deze opzet is het ook eenvoudig om meerdere zaken in één config-file op te nemen.

De eerste échte regels na het 'openen' van de XML-file geven aan dat deze actie moet worden uitgevoerd bij het aanmaken van nieuwe mailboxen of het mail-enablen van bestaande users.

XML:
1
2
<Feature Name="Mailboxes" Cmdlets="New-Mailbox,Enable-Mailbox">
<ApiCall Name="OnComplete">


Met andere woorden: bij het gebruik van new-mailbox of enable-mailbox moet iets gestart worden, en wel nadat de actie compleet uitgevoerd is. De 'feature name' tag mag je zelf invullen en is puur voor de overzichtelijkheid van je XML-file. Je kunt bijvoorbeeld blokken met zaken voor provisioning scheiden van scripts die je wilt starten als je een mailbox verwijdert.

In de volgende regels geven we aan wanneer er iets moet worden uitgevoerd en wat dat dan is:

XML:
1
2
3
4
if($succeeded) {
$Name= $provisioningHandler.UserSpecifiedParameters["Alias"]
Set-Mailbox $Name -RetentionPolicy "Retention Policy"
}



De variabele $name wordt eerst ingesteld, door de aangeroepen API, met de naam van de zojuist aangemaakt mailbox.
Vervolgens wordt een set-mailbox aangeroepen op de $name mailbox om de retention policy in te stellen.
In de script block, die wordt aangeven met de { en } symbolen, kun je reguliere powershell-code opnemen. Je kunt er echter ook voor kiezen om een externe .ps1-file met Powershell-scripts aan te roepen. Zeker als je veel gebruik gaat maken van de scripting agent en wat meer complexe code gaat gebruiken is dat bevorderlijk voor leesbaarheid van de config-file.

Aan het eind wordt het XML-document uiteraard weer netjes afgesloten.

De handeling die door de scripting agent wordt uitgevoerd zie je niet terug in (bijvoorbeeld) de Powershell-code die de EMC laat zien bij het aanmaken van de mailbox. Wel krijg je feedback in de wizard als de API call rondom de scripting agent niet goed kan worden uitgevoerd en een error geeft. In dat geval wordt de mailbox wel probleemloos aangemaakt, maar worden de extra acties vanuit de scripting agent niet doorgevoerd op de mailbox.
Als de scripting agent wel gewoon zonder problemen zijn werk kan doen, zal je direct na het aanmaken van de mailbox in de eigenschappen van de mailbox zien dat de retention policy actief is.

Je begrijpt dat je op deze manier veel zaken in Exchange kunt automatiseren. Het leukste is om hier in je testomgeving eens mee aan de slag te gaan, zodat je kunt zien wat er allemaal mogelijk is.

Voor de verbeelding nog een ander mooi voorbeeld van het gebruik van de agent:

XML:
1
2
3
4
5
6
7
8
<Feature Name="MailboxProvisioning" Cmdlets="remove-mailbox">
<ApiCall Name="validate">
if($succeeded)    {
$removedmailbox = $provisioningHandler.UserSpecifiedParameters["Name"]
New-MailboxExportRequest -Mailbox $removedmailbox -FilePath \\filserver\PSTFiles
}
</ApiCall>
</Feature>


In dit geval wordt de ApiCall 'validate' gebruikt. Bij het uitvoeren van een cmdlet checkt Exchange altijd eerst of het cmdlet valide is en of er genoeg informatie is om het cmdlet te starten. Als dit het geval is, wordt de scripting agent getriggerd. Dit gebeurt dan dus nog vóór het cmdlet daadwerkelijk wordt uitgevoerd. Wat het bovenstaande stuk code doet laat zich dus raden ;)

Laatste tip voordat je hiermee aan de slag gaat: in een multiserver omgeving moet je de scripting agent op elke exchange-server activeren en ook de xml-file op elke server beschikbaar hebben. Het makkelijkste is dan natuurlijk om die file simpelweg te kopiëren van de ene naar de andere server.

Meer informatie over de scripting agent vindt je (uiteraard) op Technet: http://technet.microsoft.com/en-us/library/dd297951.aspx

Vind de nerds in je Exchange-omgeving

Door ralpje op donderdag 19 september 2013 21:07 - Reacties (11)
Categorieën: Exchange, Office365, Powershell, Windows, Views: 5.827

Leuke oneliner in Powershell om te zien wie z'n iDevice al heeft geüpdatet naar iOS7:

PowerShell:
1
Get-MobileDevice | where {$_.DeviceOS -like "ios 7*"} | ft UserDisplayName, FriendlyName, DeviceOS, DeviceModel -A



Note: get-mobiledevice werkt alleen in Exchange 2013 (of O365 wave15). Voor Exchange 2010 of O365 wave14 gebruik je get-activesyncdevice.

Nu is dit natuurlijk een redelijke nutteloos stukje Powershell. Leuk om te zien welke fanboy al snel na het uitkomen van iOS7 heeft geüpdatet, maar niet meer dan dat. Tot je bedenkt dat er in het verleden wel eens problemen waren met Exchange en iDevices na bepaalde iOS-updates: je kunt met Powershell in zo'n geval dus eenvoudig een ActiveSync rule maken die bepaalde iOS-versies (of Android-versies. Of wat dan ook) blokkeert of in ieder geval niet meer automatisch toelaat.


PowerShell:
1
New-ActiveSyncDeviceAccessRule -QueryString &#8220;iOS 7.0&#8243; -Characteristic DeviceOS -AccessLevel Quarantine

Een alias aan een mailbox toevoegen

Door ralpje op zaterdag 18 mei 2013 00:04 - Reacties (1)
Categorieën: Exchange, Office365, Powershell, Views: 2.812

Iets wat je regelmatig doet en wat, zeker binnen Office 365, tot veel geklik leidt: het toevoegen van een alias aan een mailbox.
Omdat ik meeste beheerwerk in Office 365 via PowerShell doe maakte in een PS-Function die eenvoudig aan te roepen is om deze handeling uit te voeren.


PowerShell:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Function Add-EmailAlias {
    param($Identity, $EmailAlias)

    begin {
        $mb = Get-Mailbox $Identity
        if($mb.EmailAddressPolicyEnabled) {
            Set-Mailbox $Identity -EmailAddressPolicyEnabled $false
            $policy += 1
        }
        $addresses = $mb.EmailAddresses += $EmailAlias
    }

    process {
        Set-Mailbox $Identity -EmailAddresses $addresses
    }

    end {
        if($policy) {Set-Mailbox $Identity -EmailAddressPolicyEnabled $true}
    }
}



Simpelweg de functie aanroepen met als parameter de naam van de mailbox en de gewenste alias is genoeg.
Add-EmailAlias username alias@domein.nl

Om de functie te gebruiken dien je hem aan je PS-Profile toe te voegen. Een overzicht van de verschillende profiles, waar je ze kunt vinden en wat ze doen vind je overal op internet, bijvoorbeeld bij The Scripting Guy.

Exchange en Automapping voor mailboxen

Door ralpje op vrijdag 17 mei 2013 23:04 - Reacties (4)
Categorieën: Exchange, Powershell, Scripting, Windows, Views: 5.216

Ik ben erg blij met de 'automapping' feature die sinds SP2 in Exchange 2010 zit. Voor wie het niet weet: deze functie zorgt ervoor dat als je iemand 'full access' geeft op een mailbox de user ook gelijk deze mailbox in zijn Outlook-client te zien krijgt. Je hoeft dus niet meer de gebruiker instructies te geven hoe hij de mailbox kan openen of zelf handmatig via de accountinstellingen de extra mailbox toe te voegen.
Bij het bedenken van deze functie heeft Microsoft helaas één ding gemist: hij werkt alleen als je een gebruiker rechstreeks rechten geeft op een mailbox en niet als je dit via een Security Group doet. Jammer, want het is natuurlijk best practice om via een groep rechten uit de delen. Het bevordert de overzichtelijkheid en beheersbaarheid enorm en geeft in één oogopslag duidelijkheid over wie welke rechten heeft.
Helaas is die quirck ook in Exchange 2013 nog niet gefixt...En dus is het tijd om iets anders te verzinnen: Powershell tot the rescue!


PowerShell:
1
2
3
4
5
6
7
8
9
$shared = (get-mailbox | where {$_.IsShared -eq $True})
$shared | foreach-object {
    $mbxname = ($_.name)
    $group = "SG-MBX-Access-$mbxname"
    $users = (Get-ADGroupMember -Identity $group | select name)
    $users | foreach-object {
        add-mailboxpermission -identity $mbxname -user ($_.name) -accessrights FullAccess -automapping $true
        }
    }


Simpel maar doeltreffend en eenvoudig via Task Scheduler te plannen.

Het script zoekt elke 'shared' mailbox in de organisatie, bekijkt welke users lid zijn van de bijbehorende groep en geeft deze users rechtsreeks rechten op de shared mailbox, inclusief de 'automapping'-functie.

Op deze manier kun je dus prima nieuwe gebruikers rechten op een mailbox geven, door ze in de bijbehorende Security Group te plaatsen. Als je echter een gebruiker uit de group haalt om hem zijn rechten te ontnemen, heeft dat geen effect. Er zijn verschillende manieren om dat op te lossen, maar ik hanteer graag het KISS-principe: Keep it simpel, stupid.


PowerShell:
1
Get-MailboxPermission $mbxname | where { ($_.IsInherited -eq $False) -and ($_.User -ne 'NT Authority\self') }


Deze regel Powershell geeft een overzicht van alle mensen die nu rechten hebben op de mailbox, met uitzondering van degene waarbij die rechten 'inherited' zijn én de mailboxeigenaar zelf.
Door nu voor het zetten van de rechten deze bestaande rechten te verwijderen, weten we zeker dat de rechten gelijk zijn aan de leden van de groep. In totaal komt het script er dan zo uit te zien:

PowerShell:
1
2
3
4
5
6
7
8
9
10
11
12
13
$shared = (get-mailbox | where {$_.IsShared -eq $True})
$shared | foreach-object {
    $mbxname = ($_.name)
    $group = "SG-MBX-Access-$mbxname"
    $current = (Get-MailboxPermission $mbxname | where { ($_.IsInherited -eq $False) -and ($_.User -ne 'NT Authority\self') })
    $current | foreach-object {
        remove-mailboxpermission -identity $mbxname -user ($_.user) -accessrights FullAccess -confirm:$false
        }
    $users = (Get-ADGroupMember -Identity $group | select name)
    $users | foreach-object {
        add-mailboxpermission -identity $mbxname -user ($_.name) -accessrights FullAccess -automapping $true
        }
    }



Oké, het kan wellicht iets netter en er zit absoluut geen errorhandling in, maar het doet wat het moet doen: rechten op een mailbox zetten aan de hand van de leden van een groep. Via task scheduler plannen en elk uur (of andere naar behoefte in te stellen tijd) laten lopen en je kunt rechten op mailboxen uit (laten) delen via groupmembership en toch de automapping feature gebruiken.

Je zou er voor kunnen kiezen om een dergelijk script te implementeren als functie, zodat je alleen de functie hoeft aan te roepen met de mailbox waar je rechten op wilt zetten als parameter. Een andere opties is het script te herschrijven om een csv-file als input te nemen waarin een lijst met te bewerken mailboxen staat. Die twee opties geven in ieder geval vrijheid in het bepalen welke mailboxen je wel en niet mee wilt nemen, zonder dat het beheersbaarheid al te veel aan tast :)