PI Services

Le blog des collaborateurs de PI Services

[Exchange Hybride] Une boîte aux lettres existe à la fois dans Exchange Online et Onpremise

Description

Dans un déploiement hybride Microsoft Exchange Server, si un utilisateur ait une boîte aux lettres dans Exchange Online et une autre dans l'organisation Exchange Onpremise, ceci crée des problèmes de flux de mail. Les messages seront remis à la boîte aux lettres qui correspond à l’emplacement de l’expéditeur:

  • Si l’expéditeur se trouve Onpremise, les messages sont remis à la boîte aux lettres Onpremise.
  • Si l’expéditeur se trouve dans Exchange Online, les messages sont remis à la boîte aux lettres Exchange Online.

 

Résolution

Pour corriger ce problème de flux de messagerie, il faut garder une seule boite aux lettres, il existe donc deux solutions:

  • Conserver la boîte aux lettres Exchange Online

1. Depuis l’environnement de ligne de commande Exchange Management Shell, sauvegarder les informations de la boîte aux lettres locale dans un fichier 

$formatenumerationlimit = -1
Get-Mailbox "mailbox identity" | fl > mailboxinfo.txt

2. Déconnecter la boîte aux lettres locale

Disable-Mailbox "mailbox identity"

3. Activer l’utilisateur local en tant que boîte aux lettres distante 

Enable-RemoteMailbox "user identity" -RemoteRoutingAddress "user@contoso.mail.onmicrosoft.com"

4. Récupérer toutes les adresses proxy personnalisées sauvegardées à l'étape 1 et restaurer les. Ajouter aussi l'adresse x500 (LegacyExchangeDN)

Set-RemoteMailbox -Identity "user identity" -EmailAddresses @{add="x500:/o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=<user identity>"}

5. Récupérer les GUID des boîtes aux lettres et de la base de données :

  • GUID de la boîte aux lettres déconnectée:  noter la valeur "ExchangeGUID" dans le fichier sauvegardé à l'étape 1

  •  GUID de la base de données locale, exécuter la commande suivante:

    Get-Mailbox "user identity" | fl *ExchangeGUID*
  • GUID de la boîte aux lettres cloud, exécuter la commande suivante à l’aide de Exchange Online PowerShell

Get-Mailbox "user identity" | fl *ExchangeGUID*

6. Appliquer le GUID Exchange Online sur la boîte aux lettres distante à l’aide de l’environnement de ligne de commande Exchange Management Shell (requis pour  réintégrer la boîte aux lettres localement).

Set-RemoteMailbox "user identity" -ExchangeGuid "Exchange guid value of Exchange Online mailbox"

  • Supprimer la BAL exchange Online ou la BAL Onpremise

 

[Exchange Hybride] Les boîtes aux lettres partagées online sont automatiquement transformées en boîtes aux lettres utilisateurs

Problème

Dans un environnement Exchange Hybride et suite à une synchronisation d’annuaires, les boîtes aux lettres online de type utilisateur et qui ont été converties en boîtes aux lettres partagées peuvent revenir de manière inattendue en boîtes aux lettres normales.

 Cause

Le problème se produit parce que l’attribut "RemoteRecipientType" a été mal défini. Cet attribut doit refléter que la boîte aux lettres a été migrée ou encore qu'il s’agit d’une boîte aux lettres partagée --> Si ce n’est pas le cas, la synchronisation d’annuaire réplique les attributs dans le cloud puis la boîte aux lettres partagée sera convertie en "UserMailbox". Si l’utilisateur n'a pas de licence et si la période de grâce de 30 jours est écoulée, la boîte aux lettres sera déconnectée et convertie en "MailUser".

 Résolution

Pour résoudre ce problème, l’attribut "RemoteRecipientType" doit être mis à jour comme suit :

  1. Si la boîte aux lettres dans Exchange Online est déconnectée, attribuez temporairement une licence à l’utilisateur ce qui reconnectera automatiquement la boîte aux lettres.
  2. Si la boîte aux lettres distante n'existe pas, exécutez la commande suivante Enable-RemoteMailbox -Identity PrimarySmtpAddress -RemoteRoutingAddress TargetAddressDomain 

    (Note: La TargetAddressDomain représente votre domaine de coexistence)

  3.  Exécutez ensuite la commande suivante Set-ADUser -Identity ((Get-Recipient PrimarySmtpAddress).samaccountname) -Replace @{msExchRemoteRecipientType=100;msExchRecipientTypeDetails=34359738368}  ce qui permettra de définir qu'il s'agit d'une boîte aux lettres partagée migrée.

 

[Outlook] Le délégué ne peut pas modifier ou supprimer un élément du calendrier Outlook

Contexte et problème 

Dans Microsoft Office 365/ Office 2016, un délégué dispose des autorisations "Editeur" sur un calendrier d’un autre utilisateur dans Microsoft Outlook.

Le délégué peut créer des rendez-vous/réunions mais lorsqu’il tente de supprimer un élément du calendrier, le message d'erreur suivant s'affiche :

 

Cause

Lorsqu'un élément de calendrier est supprimé, Outlook 2016/365 tente de le déplacer vers le dossier "Éléments supprimés" de la boîte aux lettres d'origine. Si le délégué ne dispose pas des autorisations sur le dossier Éléments supprimés, l'opération échouera.

Solutions de contournement 

  • Utiliser Outlook sur le Web pour supprimer les entrées du calendrier --> Il s’agit de la solution la plus simple
  • Comme solution de contournement dans Outlook 2016/O365, attribuer au délégué au moins des autorisations d'auteur sur le dossier "Éléments supprimés"
  • Dans les versions antérieures d'Outlook, ce problème ne se pose pas puisque les éléments supprimés étaient déplacés vers la boîte aux lettres des délégués. Ce comportement est contrôlé via la clé de registre DelegateWasteBasketStyle qui se trouve sous :Ordinateur\HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\16.0\Outlook\options\general Cette clé peut avoir les valeurs : 4 – déplace les éléments supprimés vers le dossier Éléments supprimés dans la boîte aux lettres d'origine. 8 – déplace les éléments supprimés vers le dossier Éléments supprimés dans la boîte aux lettres du délégué (Par défaut, Outlook 2016 définit cette valeur clé sur 4) --> Comme une autre solution de contournement, il est possible de modifier la valeur de cette clé de registre à 8.

[Exchange Onprem] ID d'événement 4113 : Erreur inattendue lors de l'appel du service de topologie Microsoft Exchange Active Directory

L’erreur 4113 suivante: " Erreur inattendue lors de l’appel du service de topologie Microsoft Exchange Active Directory sur le serveur... Accès refusé "  est observée dans le journal des événements Application d’un serveur Exchange toutes les 15 minutes.

--> L'erreur est apparue tout simplement dans l'observateur d'évènements parce que la console Exchange PowerShell n'a pas été lancée en tant qu’administrateur avec élévation. --> Il fallait donc fermer la fenêtre de ligne de commande Exchange PowerShell en cours puis la relancer en tant qu’administrateur.

 

[Azure AD Connect] Prévention des suppressions accidentelles

Microsoft a intégré la fonction de prévention des suppressions accidentelles dans le cadre du service de provisionnement Azure AD. Lorsque le nombre de suppressions à traiter dans un cycle de provisionnement unique dépasse un seuil défini par le client, le service de provisionnement Azure AD s'interrompt en offrant une visibilité sur les suppressions potentielles afin d'accepter ou de refuser les suppressions.

La fonctionnalité de prévention des suppressions accidentelles est activée par défaut et configurée de manière à ne pas autoriser toute exportation comprenant plus de 500 suppressions. Vous pouvez modifier la valeur par défaut de 500 objets avec PowerShell en utilisant Enable-ADSyncExportDeletionThreshold, qui fait partie du module AD Sync installé avec Microsoft Entra Connect. Vous devez configurer cette valeur de manière à l’ajuster à la taille de votre organisation. Étant donné que le Planificateur de synchronisation est exécuté toutes les 30 minutes, la valeur est le nombre de suppressions détectées dans les 30 minutes.

Si le nombre de suppressions à exporter vers Microsoft Entra ID est trop important, l’exportation s’arrête et vous recevez un e-mail vous informant de l'arrêt de l'opération.

Vous pouvez également consulter l’état stopped-deletion-threshold-exceeded lorsque vous recherchez le profil d’exportation dans l’interface utilisateur Synchronization Service Manager .

 

Si vous souhaitez que tous les éléments soient supprimés, procédez comme suit :

  1. Pour récupérer le seuil de suppression actuel, exécutez l’applet de commande PowerShell Get-ADSyncExportDeletionThreshold. La valeur par défaut est 500.
  2. Pour désactiver temporairement cette protection et procéder à ces suppressions exécutez la commande PowerShell Disable-ADSyncExportDeletionThreshold.
  3. Sélectionnez par la suite le connecteur Microsoft Entra,  choisir l’action Exécuter, puis Exporter.
  4. Pour réactiver la protection, exécutez l’applet de commande PowerShell Enable-ADSyncExportDeletionThreshold -DeletionThreshold 500. Remplacez 500 par la valeur que vous avez notée lors de la récupération du seuil de suppression actuel.

Windows 11 : Script PowerShell pour activer WinRE en masse

Besoin :

Après un changement d'UPN utilisateur (environnement full Azure AD) sur un Windows 11, le reset de Windows ne fonctionne plus en mentionnant que Windows Recovery Environment (WinRE) est désactivé.

Nous avons besoin d'activer WinRE sur plusieurs devices afin de permettre le reset de Windows 11.

 

Solution :

J'ai utilisé le script PowerShell ci-dessous pour détecter si WinRE est désactivé et l'activer :

function Write-CMLogEntry {
	param (
		[parameter(Mandatory = $true, HelpMessage = "Value added to the log file.")]
		[ValidateNotNullOrEmpty()]
		[string]$Value,
		[parameter(Mandatory = $true, HelpMessage = "Severity for the log entry. 1 for Informational, 2 for Warning and 3 for Error.")]
		[ValidateNotNullOrEmpty()]
		[ValidateSet("1", "2", "3")]
		[string]$Severity,
		[parameter(Mandatory = $false, HelpMessage = "Name of the log file that the entry will written to.")]
		[ValidateNotNullOrEmpty()]
		[string]$FileName = "Enable-WinRE.log"
	)
	# Determine log file location
	$WinTemp = [System.Environment]::GetEnvironmentVariable('TEMP','Machine')
	$LogFilePath = "$WinTemp\$FileName"
	
	# Construct time stamp for log entry
	$Time = -join @((Get-Date -Format "HH:mm:ss.fff"), "+", (Get-WmiObject -Class Win32_TimeZone | Select-Object -ExpandProperty Bias))
	
	# Construct date for log entry
	$Date = (Get-Date -Format "MM-dd-yyyy")
	
	# Construct context for log entry
	$Context = $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)
	
	# Construct final log entry
	$LogText = "<![LOG[$($Value)]LOG]!><time=""$($Time)"" date=""$($Date)"" component=""Enable-WinRE.log"" context=""$($Context)"" type=""$($Severity)"" thread=""$($PID)"" file="""">"
	
	# Add value to log file
	try {
		Out-File -InputObject $LogText -Encoding Default -FilePath $LogFilePath -ErrorAction Stop
	}
	catch [System.Exception] {
		Write-Warning -Message "Unable to append log entry to $FileName file. Error message: $($_.Exception.Message)"
	}
}

#WinRE Status
if (reagentc /info | findstr "Disabled") {
	reagentc /enable
	Write-CMLogEntry -Value "Enabling Windows Recovery Environment" -Severity 2
}
else {
	Write-CMLogEntry -Value "Windows Recovery Environment is Already Enabled" -Severity 1
}

 

Intune : Déployer Visio en utilisant Intune sur une machine qui a déjà Microsoft 365 apps installé

Besoin :

Ajouter Visio sur une machine où Microsoft 365 apps est déjà installé sans désinstaller tous les binaires existants.

 

Solution :

Aller à Microsoft Intune admin center

1- cliquer sur Apps > Windows

2- cliquer sur Add et sélectionner Microsoft 365 Apps Windows 10 and later

3- sur l'onglet App suite information, donner un nom à l'application et si nécessaire une description, des notes,..

4- sur l'onglet Configure app suite, choisir le format "Enter XML data" et copier les données XML ci-dessous :

<Configuration ID="b5f8e99c-4dd4-4630-a46f-e11f8fc2a13d">
  <Add Version="MatchInstalled">
    <Product ID="VisioProRetail">
      <Language ID="MatchInstalled" TargetProduct="All" />
      <ExcludeApp ID="Groove" />
    </Product>
  </Add>
</Configuration>

 

 

 

Power BI - Exemple de Tranformation de donnée selon la valeur d'une colonne

Dans cet article, un exemple de modification customisé des champs d’une colonne en fonction de la valeur d’une autre colonne.

Dans cet exemple, celui d’une liste d’utilisateurs et de leur pays, on souhaite ajouter un préfixe au nom de l’utilisateur, en fonction de leur pays.

 

On importe les données (dans notre exemple, celui d’un fichier Texte/CSV)

 

Une fois les données importées, on sélectionne Transformer les données

On sélectionne Utiliser la première ligne pour les en-têtes pour changer le nom des colonnes 

On sélectionne Editeur avancé pour afficher la requête complète

AVANT :

 

let

    source = csv.document(file.contents("c:\users\cjourdan\documents\test_data.csv"),[delimiter=";", columns=2, encoding=1252, quotestyle=quotestyle.none]),

    #"type modifie" = table.transformcolumntypes(source,{{"column1", type text}, {"column2", type text}}),

    #"en-tetes promus" = table.promoteheaders(#"type modifie", [promoteallscalars=true]),

    #"type modifie1" = table.transformcolumntypes(#"en-tetes promus",{{"name", type text}, {"country", type text}})

 in

    #"type modifié1"

 

On ajoute à la requête les lignes ci-dessous

APRES :

 

Requête complète :

let

    Source = Csv.Document(File.Contents("C:\Users\cjourdan\Documents\Test_Data.csv"),[Delimiter=";", Columns=2, Encoding=1252, QuoteStyle=QuoteStyle.None]),

    #"Type modifié" = Table.TransformColumnTypes(Source,{{"Column1", type text}, {"Column2", type text}}),

    #"En-têtes promus" = Table.PromoteHeaders(#"Type modifié", [PromoteAllScalars=true]),

    #"Type modifié1" = Table.TransformColumnTypes(#"En-têtes promus",{{"Name", type text}, {"Country", type text}}),

  

    #"Custo Replace value" = Table.ReplaceValue(#"Type modifié1" ,each [Name],each

        if ([Country]="United State") then "US_" & Text.From([Name])

        else if ([Country]="France") then "FR_" & Text.From([Name])

        else if ([Country]="Brazil") then "BR_" & Text.From([Name])

        else if ([Country]="England") then "UK_" & Text.From([Name])

        else if ([Country]="Germany") then "GER_" & Text.From([Name])

        else if ([Country]="Spain") then "SP_" & Text.From([Name])

        else if ([Country]="Italia") then "IT_" & Text.From([Name])

        else if ([Country]="Japan") then "JP_" & Text.From([Name])

        else if ([Country]="Norway") then "NW_" & Text.From([Name])

        else if ([Country]="Russia") then "RU_" & Text.From([Name])

       

        else [Name],Replacer.ReplaceValue,{"Name"})

in

    #"Custo Replace value"

 

 

Apres application, les noms sont modifiés

 

 

 

 

 

 

 

Intune : Déployer MS Project en utilisant Intune sur une machine qui a déjà Microsoft 365 apps installé

Besoin :

Ajouter MS Project sur une machine où Microsoft 365 apps est déjà installé sans désinstaller tous les binaires existants.

 

Solution :

Aller à Microsoft Intune admin center

1- cliquer sur Apps > Windows

2- cliquer sur Add et sélectionner Microsoft 365 Apps Windows 10 and later

3- sur l'onglet App suite information, donner un nom à l'application et si nécessaire une description, des notes,..

4- sur l'onglet Configure app suite, choisir le format "Enter XML data" et copier les données XML ci-dessous :

<Configuration ID="c3089e9b-4891-4ccb-b7a1-8ac5f42ee68a">
  <Add Version="MatchInstalled">
    <Product ID="ProjectProRetail">
      <Language ID="MatchInstalled" TargetProduct="All" />
      <ExcludeApp ID="Groove" />
    </Product>
  </Add>
</Configuration>

Powershell - Utiliser ValidateScript pour valider une adresse IP en parametre

ValidateScript est un mot clé dans la declaration de paramètres d'un script ou d'une fonction, permettant de valider la valeur d'un paramètre en excutant un bloc de script qui va tester la valeur passée en paramètre. NB: il complète le mot clé ValidatePattern utilisé lui pour valider le paramètre a l'aide d'une expression regulière.

L'exemple ci-dessous est celui de la validation qu'une addresse IP donnée en paramètre est bien au format IPv4. Un message customisé est renvoyé.

 

[CmdletBinding()]
param(

[Parameter(Mandatory=$true,HelpMessage="IP cible")]
          [ValidateScript({
                           $startchar = "^"  # CARACTERE DE DEBUT DE REGEX
                           $endchar = "$"    # CARACTERE DE FIN DE REGEX
                           $ZeroOrOneTime = '?' # CHARACTERE 0 OU 1 FOIS
                           $byte = "(?:25[0-5]|2[0-4][0-9]|[01]$ZeroOrOneTime[0-9][0-9]$ZeroOrOneTime)" # REGEX CORRESPONDANT A UN NOMBRE D'UNE IP
                           $dot = '\.' # CARACTERE '.' 
                           $IPv4 = "$byte$dot$byte$dot$byte$dot$byte" # REGEX COMPLETE D'UNE IPv4
                           if($_ -match "$startchar$IPv4$endchar")
                                {
                                $true
                                } 
                                else 
                                {
                                write-host -B white -F red "$_ N'EST PAS UNE ADDRESSE IPV4 VALIDE. VEUILLEZ RENSEIGNER UNE ADRESSE AU FORMAT X.X.X.X (Ex: 192.168.0.1)"
                                EXIT 1
                                }
                          })]          
$TargetIP
)

Write-Host -F Green "$TargetIP EST UNE VALEUR CORRECTE"

 

Si la valeur renseignée est une adresse IPv4 correcte:

Si la valeur renseignée n'est pas une adresse IPv4 correcte:

 

Le scriptblock éxécuté par ValidateScript peut bien sur être reutilisé comme une fonction a part entière, en dehors du bloc de paramètres.