martedì 16 luglio 2019

Uno script per esportare l'ultimo mese della cassetta postale in archivio .PST, con Powershell - direttamente dal server Exchange con Management Shell


Buongiorno a tutti e  benvenuti ad un altro articolo del mio blog.
Oggi, riagganciandomi all’articolo precedente di Giugno, sul metodo di esportare una cassetta postale dal server con new-mailboxexportrequest, voglio fornirvi uno script bello e pronto per schedulare l’archiviazione del mese precedente alla data di esecuzione.
Ovviamente, poi, potete modificarlo per ampliare o restringere il periodo, accettare le date di inizio e di fine dai parametri…. …e chi più ne ha, più ne metta.

Bando alle ciance, dunque: ecco lo script, prontissimo per essere messo in un ps1 e lanciato a piacimento, serialmente...



Param(
    [parameter(Mandatory=$true, HelpMessage="indirizzo principale della mailbox da archiviare")]
    [string]$address,
    [parameter(Mandatory=$true, HelpMessage="indica il percorso DI RETE dove salvare il PST (verrà appesa una sottocartella con l'anno)")]
    [string]$baseP
)
import-module activeDirectory
if ( (Get-PSSnapin -Name Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction SilentlyContinue) -eq $null ){
    add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010
}

# check di esistenza mailbox
if (-not ($mb=Get-mailbox $address -ErrorAction silentlycontinue)) {
write-host "la casella specificata non esiste"
exit
}

$a=get-date
# ottengo il primo giorno del mese precedente
$b=get-date(""+($a.AddMonths(-1)).Month+"/"+$a.Year)
$nomeMese=$b.Month | %{(Get-Culture).DateTimeFormat.GetMonthName($_)}
# ottengo il primo giorno del mese corrente
$c=get-date(""+$a.Month+"/"+$a.Year)

if (-not(test-path -PathType Container ($baseP+"\"+$b.Year))){
    New-Item -ItemType Directory -Force -Path ($baseP+"\"+$b.Year)
}
$path = $baseP+"\"+$b.Year+"\"+($mb.Name+"_"+$nomeMese+"-"+$b.Year)+".pst"

new-mailboxExportRequest $mb -FilePath $path -ContentFilter {(received -gt $b) -and (received -lt $c)} -Name ($mb.Name+"--"+$nomeMese+$b.Year)


martedì 9 luglio 2019

Exchange Powershell (EMS) - aggiungere più utenti ad un gruppo di distribuzione con una riga sola di comando.

Buongiorno a tutti,
 oggi dovevo aggiungere più utenti e rimuoverne altri da un distribution group di Exchange.
Come sapete cerco di fare tutto dalla shell powershell di management (Exchange Management Shell), ma stavolta è stato "tricky".

Ho trovato una soluzione elegante e quindi scrivo questo mini-post....

Pronti? via.

Per aggiungere più membri contemporaneamente ad un gruppo di distribuzione con PowerShell

"utente1", "utente2", "utente3" | Add-DistributionGroupMember nomeDG@dominio.it -BypassSecurityGroupManagerCheck

con questo trick one-liner (come si dice "nel gergo di noi scriptomani") possiamo evitare la sintassi con il ciclo foreach, le graffe etc...
Elegante, no?




Per rimuovere più membri contemporaneamente da un gruppo di distribuzione con PowerShell

"utente1", "utente2", "utente3" | Remove-DistributionGroupMember nomeDG@dominio.it -BypassSecurityGroupManagerCheck

... in sostanza la stessa cosa.


Beh, che dire: vi ho promesso un articolo "mini", no?
Spero di aver facilitato la vita a qualche sysadmin e stuzzicato la vostra fantasia.

Happy Scripting a tutti!

martedì 25 giugno 2019

Esportare una cassetta postale in PST (archiviare) con POWERSHELL, direttamente dal server Exchange con Powershell EMS


EDIT: Se volete uno script pronto per archiviare una cassetta postale, che accetta parametri etc...
... sto lavorando a un nuovo POST.
Iscrivetevi come follower al BLOG (o con il vostro lettore di notizie RSS): verrete avvisati quando pubblicherò l'articolo! :-)

A volte ci si trova nella necessità di creare delle esportazioni di cassetta postale in file che possano essere esaminati o aperti in momenti successivi.
É il caso, ad esempio, di quando si hanno lavoratori remoti (smart-working o personale con funsioni principalmente esterne) e una connessione “limitata”.
Il problema che si pone in quel caso è che archiviare un PST su un file server (perché possa essere sottoposto a backup e non sia a rischio di rottura del disco fisso del PC) risulta un’operazione che satura tutta la larghezza di banda… Questo perché il client deve tenere aperto il file e, non so perché in dettaglio, manda dati avanti e indietro come un pazzo…
La soluzione quick & dirty sarebbe di creare il file PST in locale e poi spostarlo sul server… tuttavia anche questa ha parecchie limitazioni, così ovvie che non le scrivo qui.

Quindi, cosa fare per essere sicuri di archiviare automaticamente le mail in una cassetta postale con powershell (Exchange Management Console) in Microsoft Exchange Server 2010 SP3 (ma anche nelle versioni successive)?

Ecco i comandi che ci possono essere utili:
  • New-MailboxExportRequest (iniziare la richiesta di esportazione)
  • Get-MailboxExportRequest (ottenere le richieste di esportazione in corso, e anche terminate)
  • Get-MailboxExportRequestStatistics (ottenere le satistiche circa l’avanzamento della richiesta)
  • Remove-MailboxExportRequest (rimuove la request dalla coda, utile soprattutto per far pulizia).



Premesse

Caricare la snap-in di exchange (ovvio)

Tutti questi comandi vanno lanciati da una powershell con la snap-in di Microsoft Exchange, quindi, se avete una powershell normale, lanciate:
if ( (Get-PSSnapin -Name Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction SilentlyContinue) -eq $null ){
    add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010
}

E avrete la snap-in amministrativa caricata.

Non si può salvare in un percorso LOCALE del server

 deve essere una share di rete (anche se potete usare tranquillamente \\NOMESERVER\C$)
(o almeno IO non posso, dato che ho un DAG – un cluster di exchange – ma suppongo sia così per tutti).

Accesso share di destinazione

La share di rete deve permettere l’accesso in scrittura agli account COMPUTER dei server Exchange nella vostra organizzazione.
Quindi, autorizzate, altrimenti riceverete un bel “Access Denied”


FINE DELLE PREMESSE




Esportazione di tutta la cassetta postale

Ve la faccio breve: ecco come fare se si vuole esportare TUTTO il contenuto della cassetta postale.

New-MailboxExportRequest cassetta.postale@dominio.it -FilePath "\\server\share\...\archivioCassettaPostale.pst"

E tutto verrà salvato.


Le opzioni di New-MailboxExportRequest

Ecco qui le opzioni da get-help del comando.
New-MailboxExportRequest
   [-Mailbox]
   -FilePath
   [-AcceptLargeDataLoss]
   [-AssociatedMessagesCopyOption ]
   [-BadItemLimit ]
   [-BatchName ]
   [-CompletedRequestAgeLimit ]
   [-Confirm]
   [-ConflictResolutionOption ]
   [-ContentFilter ]
   [-ContentFilterLanguage ]
   [-DomainController ]
   [-ExcludeDumpster]
   [-ExcludeFolders ]
   [-IncludeFolders ]
   [-InternalFlags ]
   [-IsArchive]
   [-LargeItemLimit ]
   [-MRSServer ]
   [-Name ]
   [-Priority ]
   [-RemoteCredential ]
   [-RemoteHostName ]
   [-SkipMerging ]
   [-SourceRootFolder ]
   [-Suspend]
   [-SuspendComment ]
   [-TargetRootFolder ]
   [-WhatIf]
   [-WorkloadType ]
   []

Focalizziamoci per ora solo su:
-BadItemLimit
-ContentFilter
-ContentFilterLanguage

BadItemLimit: consentire l’errore su N items e proseguire

Con questa opzione possiamo specificare quanti errori “tollerare” prima che il job fallisca. In base alle necessità si può variare il numero, tuttavia ho riscontrato che i job lanciati con il default falliscono un po’ troppo spesso. Quindi, se vogliamo tollerare un po’ di perdita, per consentirci di lanciare il comando unattended, sarà bene aumentare un po’…

ContentFilter: un filtro per selezionare i messaggi da esportare

… e qui abbiamo a disposizione un intero arsenale di opzioni, pari quasi alle proprietà dei singoli messaggi….
Prendiamo ad esempio questo filtro
{(body -like "*company*") -and (body -like "*profit*") -and (Received -lt "01/01/2018")}
Ci consente di esportare solo i messaggi che contengono le parole company e profit nel corpo del messaggio e che sono ricevuti prima del primo Gennaio 2018.

ContentFilterLanguage : possiamo specificare la localizzazione (culture) del filtro

A che mi serve, direte voi?
Perché se usate filtri con il nome delle cartelle o con il nome di determinati altri parametri che sono soggetti a localizzazione (quindi hanno un nome diverso se la cassetta postale è impostata per la localizzazione in Italiano, rispetto che in inglese, ad esempio), dovrete specificare il che “culture” è il filtro che usate…

se avete bisogno di altre opzioni ecco il link ufficiale (non cambia da Exchange 2010 al 2019):

Esportazione di una cassetta postale con Filtro.

Ecco un classico filtro che potete applicare per un job di archiviazione ricorsivo. Usiamo le date con le operazioni, per identificare un intervallo ed esportare solo le mail inviate o ricevute entro quell’intervallo.
$a = get-date

New-MailboxExportRequest cassetta.postale@dominio.it -FilePath "\\server\share\...\archivioCassettaPostale.pst" -ContentFilter {(received -lt (get-date).AddDays(-60).AddHours(-1*$a.Hour).AddMinutes(-1*$a.Minute)) -and (received -gt (get-date).AddDays(-90).AddHours(-1*$a.Hour).AddMinutes(-1*$a.Minute) )}

Come vedete, memorizzo prima la data attuale in $a, quindi vado a togliere le ore e i minuti dalla data, per riportarla a mezzanotte del giorno selezionato.
Qui selezioniamo tutti i messaggi inviati o ricevuti tra i 60 e i 90 giorni prima dalla data attuale, con
received -lt (less than) e received -gt (greater than)



Alcuni Trucchetti

ottenere il primo ed ultimo giorno del mese corrente (o di una data in mezzo al mese), con Powershell

Primo giorno del mese in powershell

$a.AddDays(-1*($a.Day-1))
#se invece il mese è definito (SO già che è MARZO):
get-date("03/2019")

Ultimo giorno del mese in powershell

(Get-date(($a.Month+1).ToString()+/+$a.Year.ToString())).AddDays(-1)


Questi esempi assumono $a = una data ottenuta con get-date

Fare Pulizia delle mailboxExportRequest

Come vedete nello screenshot più in basso, le richieste, anche quando vengono completate, rimangono in “storico”. Questo per consentire a chi dovesse lanciarne molte di visualizzarne i risultati.
Ecco quindi come fare pulizia, una volta che sono tutte terminate, in un modo o nell’altro:
get-MailboxExportRequest | Remove-MailboxExportRequest


ed ecco il risultato




Bene, spero di aver stuzzicato anche oggi la vostra voglia di scriptare... vi mando il solito augurio:

HAPPY SCRIPTING A TUTTI!

lunedì 1 aprile 2019

BONUS POST - dsget, dsquery, dsadd, dsmod - usare dsadd per creare molti gruppi di sicurezza in una specifica OU

Buongiorno a tutti e benvenuti a questo nuovo post del blog.
Il mini-post di quest'oggi è una sorta di BONUS TRACK della miniserie sugli strumenti di gestione di Active Directory da riga di comando di cui posto qui i links:



Oggi ho avuto la possibilità di applicare nella realtà un dei miei post recenti riguardo DSADD. Ho avuto la necessità di:

creare più security groups da un elenco, in una specifica OU, con uno script



Mi sono trovato in una situazione in cui dovevo creare numerosi gruppi, di tipo "domain local-security" sotto una specifica Organizational Unit di AD.

Ed ecco che ho "risfoderato" l'articolo che avevo scritto e messo un comando in un FOR:

FOR /F "usebackq delims=;" %a IN ("ADGRoups_daCreare.txt") DO @dsadd group "CN=%a,OU=PEC_MailDocPro,OU=xPermissions,OU=RUOLI,DC=DASItalia,DC=local" -secgrp yes -scope l -desc "gruppo di profilo sicurezza per maildocpro"

Ed ecco il nostro bellissimo risultato: gruppi creati in pochi secondi.
Gli errori sono derivati dal fatto che alcuni gruppi li avevo già creati manualmente.

HAPPY SCRIPTING a tutti!

martedì 12 febbraio 2019

Le potenti hash table di powershell - episodio 2


Buongiorno e benvenuti a questo secondo ed ultimo episodio di questa mini-serie dedicata alle hash table di Powershell.
In questa “puntata” dedicherò un po’ di tempo ai possibili modi di interagire ciclicamente con una hashtable.
Se non avete letto il primo episodio di questa mini-serie, vi consiglio di andare a leggervelo qui.
Detto questo, mettiamoci all’opera.

Ciclare su una hash table – non così diretto

Come potete notare, una hashtable non è una collection di oggetti nuda e cruda, ma ogni voce della hashtable ha un nome e un valore.




Come vedete dalle prove che  ho fatto in questa immagine, non è possibile NEMMENO richiamare la proprietà Name o Value di ogni foreach, mentre è necessario utilizzare il metodo getEnumerator()

GetEnumerator() … …con qualche limite

Ecco quindi venirci in aiuto il GetEnumerator che, tuttavia, impone qualche limite sull’uso di oggetti come chiave. Vedete nell’immagine sopra che, quando il ciclo è arrivato sul punto in cui la chiave è un oggetto , lo script è andato in errore…
Quindi va benissimo usare questo sistema con hashtable testuali, ma quando abbiamo chiavi particolari, meglio usare il metodo che segue…

Il metodo migliore, secondo me: usiamo il metodo .Keys

Con il metodo .Keys della classe HashTable, otteniamo un array di tutte le chiavi. Utilizzando poi la notazione con le parentesi quadre per recuperare i valori dalla array, possiamo ciclare in tutta libertà sulla nostra hashtable.







Bene! Anche per oggi spero di aver stuzzicato la vostra voglia di scriptare e la vostra fantasia.
Happy Scripting a tutti!

domenica 10 febbraio 2019

Le potenti hash table di powershell - episodio 1


Buongiorno a tutti e benvenuti in un nuovo POST del mio blog: “Lo scriptomane”
Oggi parliamo di un bellissimo costrutto del linguaggio powershell: le hash table (o hashtable, le chiamano tutti in entrambi i modi).
Le hash tables sono delle array associative in powershell. Quindi sono delle array con un indice non numerico, ma che può essere testuale, senza un ordine prefissato (salvo creare una [ordered] hashtable, vedere link di approfondimento + avanti), ma che accetta l’inserimento di variabili associate ad un index (o chiave, key).
In powershell le hash tables non sono un tipo di variabile, ma ognuna di esse è un’istanza dell’oggetto System.Collections.Hashtable ed è quindi possibile richiamare ed utilizzare i metodi di quest’oggetto.
Per approfondire potete andare sul sito di microsoft qui.
La sintassi delle hashtable è questa:

@{ = ; [ = ] ...}

Sicchè, come avrete capito, vi possono essere infinite coppie di chiave – valore.
Quest’oggetto veramente interessante di powershell ha una caratteristica molto potente:

Ogni valore può essere qualsiasi cosa: un’oggetto, un’istanza di oggetto, una variabile, un’altra hashtable, etc… INOLTRE ogni chiave, a sua volta, può essere qualsiasi cosa (questo non molti lo immaginano), con poche limitazioni che ne derivano.





In questo senso assomigliano moltissimo alle array di PHP (altro linguaggio a me caro).
Stando così le cose, mi sembra chiaro che, con un po’ di fantasia, è possibile creare oggetti per stoccare ordinatamente un po’ qualsiasi cosa, davvero con pochissime limitazioni.



Come creare una Hash Table in powershell

A questo punto, direi di vedere come si crea una Hash Table. La sintassi è semplicissma:

$toMap = @{}

In questo modo avremo creato una hash table vuota. Oppure

$toMap = @{“chiave”=”valore”; “chiave2”=”valore2”}

… per creare una HashTable già “popolata”.




Come inserire un valore nuovo in una Hash Table in powershell

La sintassi è veramente semplice, si usano le parentesi quadre, indicando la chiave.

$toMap["chiaveNuova"]="valore" # con chiave testuale e valore testuale
$toMap["chiaveNuovissima"]=$user # con chiave testuale e valore oggetto o variabile
$toMap[$user]="prova" # con chiave oggetto o variabile e valore testuale

Se l’indice era già presente all’interno della Hash Table, il valore che viene indicato verrà sostituito, se invece non era presente, il nuovo elemento verrà aggiunto.


Come recuperare un valore da una HashTable in powershell

Ed ecco che, una volta inseriti i valori vogliamo recuperarli… questo è semplicissimo e si può fare con

$toMap["chiaveNuova"]

Ma ecco che c’è subito una casistica particolare e interessante: se usiamo, come chiave, un oggetto, possiamo recuperare il valore anche utilizzando un oggetto identico?

Vi faccio un esempio: da una parte devo usare una hash per immagazzinare determinati parametri di configurazione per un utente, quindi uso l’utente come chiave e il valore sarà un’altra hash table.
Giacché poi ciclo e recupero gli utenti, inserendoli in variabile, posso recuperare i valori di configurazione precedentemente immagazzinati?

Ecco qui un’immagine che vi farà capire meglio cosa intendo:


come vedete, purtroppo, le istanze degli oggetti ADUser non sono IDENTICHE in tutto e per tutto, quindi non possiamo usare questo tipo di oggetto per recuperare un valore da una chiave rappresentata da un’istanza di ADUser dello stesso utente.
Tuttavia, se gli oggetti sono IDENTICI, allora è possibile. Guardate quest’immagine:





… per oggi Finisco qui, dedicherò un approfondimento a brevissimo su come si può ciclare su di un oggetto HashTable.


Happy Scripting a Tutti!

venerdì 8 febbraio 2019

Utilizzare una funzione definita in un file di Powershell


Buongiorno a tutti e benvenuti nuovamente nel mio blog!
Capita spesso, in programmazione e così anche negli script, di creare delle funzioni generiche che ci piacerebbe riutilizzare.

Nei linguaggi di scripting, tuttavia, non sempre è intuitivo capire come “includere” le definizioni di funzioni: ogni linguaggio ha il suo “dialetto”…

Ecco, in breve, come sfruttare questa buona pratica di programmazione, il riutilizzo del codice, con powershell.

Supponiamo, ad esempio, di aver scritto questa funzione dentro un file “FUNZIONI.ps1”:

function echoMyName($user){
      write-host "Ciao, $($user.Name), questo e' il tuo nome e quest'altro e' il tuo ufficio: $($user.Office) "
}

… e di volerla riutilizzare per la nostra amministrazione via powershell o in nuovi script, per riutilizzo del codice (ottima pratica).
Quindi? Ecco qui di seguito alcuni trick per farlo:

Richiamare una function dal file da linea di comando shell

È presto fatto, basta ricordarsi una notazione un po’ particolare. Dobbiamo cominciare con una & e una parentesi graffa, dentro quest’ultima un punto prima del percorso del file che contiene la funzione, quindi il percorso, un punto e virgola (che separa il comando successivo) e
·         La & serve per indicare a powershell che stiamo per eseguire un comando.
·         La parentesi graffa serve per “racchiudere” i comandi in un unico “set di comandi”.
·         Il punto serve per indicare che vogliamo “eseguire” il file PS1 (che conterrà solo definizioni di funzione, quindi non avrà alcun output).
Mi raccomando lo spazio tra il primo punto e il percorso del file
Come di seguito:

& { ..\Funzioni.ps1”;  echoMyName((get-aduser DiegoC))}



Come usare una funzione powershell scritta in un file, su di un loop

Per utilizzare una funzione powershell da un file, però sugli oggetti di un loop, magari i risultati in una collection derivanti da un comando che  abbiamo lanciato.
Per farlo è sufficiente includere i comandi dentro le graffe all’interno di quelle del foreach o del for, così:
get-Aduser -Filter*| foreach { ..\Funzioni.ps1”;  echoMyName($_) }



Come includere un file .ps1 che contiene delle funzioni

Per includere ed usare, in uno script powershell, delle funzioni che avete definito in UN ALTRO file…
È sufficiente usare questa notazione:
. ".\FUNZIONI.ps1"
Da quel momento in avanti potrete usare le funzioni che avete scritto


Bene, spero di aver solleticato anche oggi un po’ la vostra fantasia…
Happy Scripting a tutti!