Exchange et Powershell : duplication de mailbox

Ayant remis un peu les mains sur Exchange, j'ai dû réaliser la création de multiples Equipment Mailbox ; voilà qui était donc une excellente idée de départ pour mon tout premier script Powershell pour Exchange.

Ce script permet donc de dupliquer une mailbox existante vers une autre, en gardant la quasi-totalité de ses propriétés et privilèges accordés. Ensuite, le script gère les paramètres propres aux réservations, mais il est bien évidemment possible de mettre cette portion en commentaire.

Write-Host "Equipment Mailbox Duplication Script"
Write-Host "====================================`r`n"
$srcmbxname = Read-Host "Source Equipment Mailbox Name or Alias"
$srcmbx = Get-Mailbox $srcmbxname -ErrorAction Stop
$srccal = Get-CalendarProcessing $srcmbxname -ErrorAction Stop
$srcperm = Get-MailboxPermission $srcmbxname
$newmbxname = Read-Host "New Equipment Mailbox Name"
$newmbxalias = Read-Host "New Equipment Mailbox Alias"
$newmbxaddr = Read-Host "New Equipment Mailbox Address"
Write-Host "Creating the new mailbox…"
New-Mailbox -Name $newmbxname -Alias $newmbxalias -UserPrincipalName $newmbxaddr -SamAccountName $newmbxalias -OrganizationalUnit $srcmbx.OrganizationalUnit -Equipment -ErrorAction Stop
Write-Host "Copying basic mailbox properties."
Set-Mailbox $newmbxalias -UseDatabaseRetentionDefaults $srcmbx.UseDatabaseRetentionDefaults -RetainDeletedItemsUntilBackup $srcmbx.RetainDeletedItemsUntilBackup -GrantSendOnBehalfTo $srcmbx.GrantSendOnBehalfTo -DeliverToMailboxAndForward $srcmbx.DeliverToMailboxAndForward -LitigationHoldEnabled $srcmbx.LitigationHoldEnabled -SingleItemRecoveryEnabled $srcmbx.SingleItemRecoveryEnabled -RetentionHoldEnabled $srcmbx.RetentionHoldEnabled -CalendarRepairDisabled $srcmbx.CalendarRepairDisabled -MessageTrackingReadStatusEnabled $srcmbx.MessageTrackingReadStatusEnabled -RetainDeletedItemsFor $srcmbx.RetainDeletedItemsFor -RecipientLimits $srcmbx.RecipientLimits -AntispamBypassEnabled $srcmbx.AntispamBypassEnabled -CalendarVersionStoreDisabled $srcmbx.CalendarVersionStoreDisabled -HiddenFromAddressListsEnabled $srcmbx.HiddenFromAddressListsEnabled -MaxSendSize $srcmbx.MaxSendSize -MaxReceiveSize $srcmbx.MaxReceiveSize -EmailAddressPolicyEnabled $srcmbx.EmailAddressPolicyEnabled -RequireSenderAuthenticationEnabled $srcmbx.RequireSenderAuthenticationEnabled -SendModerationNotifications $srcmbx.SendModerationNotifications -ExternalOofOptions $srcmbx.ExternalOofOptions -ProhibitSendQuota $srcmbx.ProhibitSendQuota -ProhibitSendReceiveQuota $srcmbx.ProhibitSendReceiveQuota -DowngradeHighPriorityMessagesEnabled $srcmbx.DowngradeHighPriorityMessagesEnabled -UseDatabaseQuotaDefaults $srcmbx.UseDatabaseQuotaDefaults -IssueWarningQuota $srcmbx.IssueWarningQuota -RemoteRecipientType $srcmbx.RemoteRecipientType -AuditEnabled $srcmbx.AuditEnabled -AuditLogAgeLimit $srcmbx.AuditLogAgeLimit -AuditAdmin $srcmbx.AuditAdmin -AuditDelegate $srcmbx.AuditDelegate -AuditOwner $srcmbx.AuditOwner -AcceptMessagesOnlyFromDLMembers $srcmbx.AcceptMessagesOnlyFromDLMembers -ArbitrationMailbox $srcmbx.ArbitrationMailbox -BypassModerationFromSendersOrMembers $srcmbx.BypassModerationFromSendersOrMembers -CustomAttribute1 $srcmbx.CustomAttribute1 -CustomAttribute2 $srcmbx.CustomAttribute2 -CustomAttribute3 $srcmbx.CustomAttribute3 -CustomAttribute4 $srcmbx.CustomAttribute4 -CustomAttribute5 $srcmbx.CustomAttribute5 -CustomAttribute6 $srcmbx.CustomAttribute6 -CustomAttribute7 $srcmbx.CustomAttribute7 -CustomAttribute8 $srcmbx.CustomAttribute8 -CustomAttribute9 $srcmbx.CustomAttribute9 -CustomAttribute10 $srcmbx.CustomAttribute10 -CustomAttribute11 $srcmbx.CustomAttribute11 -CustomAttribute12 $srcmbx.CustomAttribute12 -CustomAttribute13 $srcmbx.CustomAttribute13 -CustomAttribute14 $srcmbx.CustomAttribute14 -CustomAttribute15 $srcmbx.CustomAttribute15 -ExtensionCustomAttribute1 $srcmbx.ExtensionCustomAttribute1 -ExtensionCustomAttribute2 $srcmbx.ExtensionCustomAttribute2 -ExtensionCustomAttribute3 $srcmbx.ExtensionCustomAttribute3 -ExtensionCustomAttribute4 $srcmbx.ExtensionCustomAttribute4 -ExtensionCustomAttribute5 $srcmbx.ExtensionCustomAttribute5 -ModeratedBy $srcmbx.ModeratedBy -ModerationEnabled $srcmbx.ModerationEnabled -RejectMessagesFromDLMembers $srcmbx.RejectMessagesFromDLMembers -SimpleDisplayName $srcmbx.SimpleDisplayName -MailTip $srcmbx.MailTip -MailTipTranslations $srcmbx.MailTipTranslations -ErrorAction Stop
Write-Host "Done. Now copying calendar processing properties."
Set-CalendarProcessing -Identity $newmbxalias -AutomateProcessing $srccal.AutomateProcessing -AllowConflicts $srccal.AllowConflicts -BookingWindowInDays $srccal.BookingWindowInDays -MaximumDurationInMinutes $srccal.MaximumDurationInMinutes -AllowRecurringMeetings $srccal.AllowRecurringMeetings -EnforceSchedulingHorizon $srccal.EnforceSchedulingHorizon -ScheduleOnlyDuringWorkHours $srccal.ScheduleOnlyDuringWorkHours -ConflictPercentageAllowed $srccal.ConflictPercentageAllowed -MaximumConflictInstances $srccal.MaximumConflictInstances -ForwardRequestsToDelegates $srccal.ForwardRequestsToDelegates -DeleteAttachments $srccal.DeleteAttachments -DeleteComments $srccal.DeleteComments -RemovePrivateProperty $srccal.RemovePrivateProperty -DeleteSubject $srccal.DeleteSubject -AddOrganizerToSubject $srccal.AddOrganizerToSubject -DeleteNonCalendarItems $srccal.DeleteNonCalendarItems -TentativePendingApproval $srccal.TentativePendingApproval -EnableResponseDetails $srccal.EnableResponseDetails -OrganizerInfo $srccal.OrganizerInfo -ResourceDelegates $srccal.ResourceDelegates -RequestOutOfPolicy $srccal.RequestOutOfPolicy -AllRequestOutOfPolicy $srccal.AllRequestOutOfPolicy -BookInPolicy $srccal.BookInPolicy -AllBookInPolicy $srccal.AllBookInPolicy -RequestInPolicy $srccal.RequestInPolicy -AllRequestInPolicy $srccal.AllRequestInPolicy -AddAdditionalResponse $srccal.AddAdditionalResponse -AdditionalResponse $srccal.AdditionalResponse -RemoveOldMeetingMessages $srccal.RemoveOldMeetingMessages -AddNewRequestsTentatively $srccal.AddNewRequestsTentatively -ProcessExternalMeetingMessages $srccal.ProcessExternalMeetingMessages -RemoveForwardedMeetingNotifications $srccal.RemoveForwardedMeetingNotifications -ErrorAction Stop
Write-Host "Done. Now setting up permissions."
foreach($perm in $srcperm){
if($perm.Deny.IsPresent -eq $false) { Add-MailboxPermission -Identity $newmbxalias -User $perm.User -AccessRights $perm.AccessRights }
else { Add-MailboxPermission -Identity $newmbxalias -User $perm.User -AccessRights $perm.AccessRights -Deny }
}
Write-Host "All done. Successfully created mailbox"$newmbxalias

Le script est téléchargeable dans une version commentée.

Powershell : listing et filtre des tâches planifiées sur de multiples serveurs

Ayant pu enfin appliquer par GPO un traitement que j'effectuais par script appelé par une tâche planifiée quotidiennement, j'ai dû procéder au listing complet des serveurs sur lesquels cette tâche était créée afin de pouvoir procéder à sa suppression. Ne pouvant pas me connecter un à un sur chaque machine pour vérifier la présence ou non, j'ai dû concevoir un script me permettant de lister les tâches planifiées qui portent mon compte d'utilisateur en auteur sur tout un scope de serveurs.

Voici le snippet que j'ai écrit :

$complist = Get-AdComputer -filter * -SearchBase "OU=SERVERS,DC=DUNDERMIFFLIN,DC=local"
foreach ($comp in $complist) {
Write-Host "`r`nServer:"$comp.DNSHostName
Invoke-Command -ComputerName $comp.DNSHostName -ScriptBlock { Get-ScheduledTask | Where-Object { $_.Author -eq "DUNDERMIFFLIN\mscott" } }
}

Ce script renvoie donc toutes les tâches planifiées que l'utilisateur mscott a créé sur chacun des serveurs présents dans la liste retournée par la requête AD. Il est possible de moduler ce que l'on souhaite obtenir en changeant le filtre ou le chemin LDAP dans la requête Get-AdComputer, sans oublier les propriétés des tâches planifiées dans la boucle Where-Object.

A noter que Get-ScheduledTask ne fonctionne qu'à partir de Windows Server 2012. Si jamais l'instruction Invoke-Command renvoie un message d'erreur propre à WinRM et que le contenu de l'instruction fonctionne en local, il vous faut activer l'administration Powershell à distance grâce à la commande Enable-PSremoting.

Powershell : désinstallation des hotfix Windows installés à une date spécifiée

J'ai développé pour le compte d'un collègue un script Powershell permettant de lister les hotfix Windows installés à une date spécifiée par l'utilisateur et proposant de les désinstaller, par exemple dans le cadre d'une campagne de patch mal déroulée sur une station ou un serveur.

Write-Host "Windows Operating System Hotfix KB remover"
Write-Host "==========================================`r`n"
$instdate = Read-Host "Please input the date KB were installed on."
$instdate = $instdate -as [datetime]
if($instdate -eq $null) { Write-Host "Please input a valid date.`r`nBye." ; exit }
$hotfixlist = Get-Hotfix | Where-Object { $_.InstalledOn -eq $instdate }
if($hotfixlist -eq $null) { Write-Host "No KBs installed for the specified date.`r`nBye." ; exit }
Write-Host "KB list for"$instdate
echo $hotfixlist
$choice = Read-Host "`r`nDo you want to uninstall those KBs ? (y to uninstall - any other input will cancel)"
if($choice -eq "y") {
foreach($kb in $hotfixlist){
$kbid = $kb.HotfixID.Substring(2,$kb.HotfixID.Length-2)
Write-Host "Uninstalling"$kbid
wusa.exe /uninstall /KB:$kbid /norestart
Write-Host "Please press ENTER only once the update wizard is closed because only one instance of it can be started at a time." -ForegroundColor "Yellow"
Read-Host
}
}
Write-Host "Bye."

Le script récupère la liste des patchs - Windows uniquement - installés le jour spécifié, extrait le numéro de KB et passe ensuite en argument de la ligne de commande de désinstallation de paquets Windows Update ce numéro en évitant un redémarrage.

Une pause marquée par le Read-Host est obligatoire après l'exécution de la commande de désinstallation sinon le script les exécute toutes à la suite, ce qui ne fonctionne pas puisque seule une instance de wusa.exe peut être lancée en même temps.

Une version commentée du script est disponible en téléchargement.

The account is not authorized to login from this station

J'ai obtenu cette erreur en tentant de monter un partage réseau d'une machine en Windows XP sur un Server 2016, suite à une migration d'un Server 2003 sur lequel tout fonctionnait bien.

Après comparaison de multiples clefs de registre sur des machines Windows XP similaires pour lesquelles la connexion du partage réseau était possible, j'ai fait chou blanc ; jusqu'à tomber sur ce réglage de GPO locale (les machines sont toutes en standalone, je n'ai donc pas de politique de domaine) :

Situé dans Configuration Ordinateur > Paramètres Windows > Paramètres de sécurité > Stratégies locales > Options de sécurité > Accès réseau : modèle de partage et de sécurité pour les comptes locaux, ce paramètre était réglé sur Classique pour la machine incriminée et sur Guest (invité) sur toutes les autres. Une fois le paramètre passé sur Guest, j'ai pu monter ce partage SMB.

Il semblerait donc que les versions suivantes de SMB présentes avec 2016 (3.1+) aient logiquement des politiques de sécurité renforcées par rapport au SMB 1.x livré avec 2003. Ce billet d'un blog TechNet offre plus d'informations concernant les évolutions de Server Message Block.

Powershell : harmonisation de la configuration DNS

Afin d'harmoniser la configuration DNS sur des serveurs situés sur différents sites géographiques, j'ai développé un script Powershell lié à une GPO qui l'exécute au démarrage.

Ce script récupère grâce au nom du serveur son emplacement et applique en fonction de ce dernier les bonnes adresses IP. Naturellement, il faudra adapter l'extraction de l'emplacement dans le nom de la machine en fonction de l'environnement sur lequel le script sera déployé ; de plus, le domaine sur lequel la carte est connectée sert d'identification de la carte réseau employée pour la résolution DNS.

$location = (hostname).Substring(2,3)
switch($location){
"PAR" { $dnslist = '("192.168.1.1","192.168.1.2","192.168.2.1")' }
"BXL" { $dnslist = '("192.168.2.1","192.168.2.2","192.168.1.1")' }
"BER" { $dnslist = '("192.168.3.1","192.168.3.2","192.168.2.1")' }
"LDN" { $dnslist = '("192.168.4.1","192.168.4.2","192.168.3.1")' }
"NYC" { $dnslist = '("192.168.5.1","192.168.1.1","192.168.4.1")' }
}
$cards = Get-NetIPConfiguration
foreach($card in $cards){
if($card.NetProfile.Name -eq "dundermifflin.local") { $index = $card.InterfaceIndex }
}

$query = "Set-DnsClientServerAddress -InterfaceIndex $index -ServerAddresses $dnslist"
Invoke-Expression $query

Ce snippet fonctionne si par exemple mes serveurs ont une nomenclature de nommage comme celle-ci : <OS><Environnement><Emplacement><Rôle><ID>, qui pourrait donner par exemple pour un contrôleur de domaine de production WPLDNDC01 ou un serveur PostgreSQL de recette LRBXLPG03.

Ce script est disponible dans une version commentée sur le miroir de téléchargement.