Limitation sur le choix des partages réseaux utilisables pour héberger les « User Profile Disks » (UPDs) de Remote Desktop Services.

Lorsqu'on lit la documentation de RDS concernant la fonctionnalité des UPDs, le type de partage réseaux à utiliser n'est pas décrit avec une grande précision.
La plupart du temps on peut lire :
https://blogs.technet.microsoft.com/enterprisemobility/2012/11/13/easier-user-data-management-with-user-profile-disks-in-windows-server-2012/
User profile disks can be stored on Server Message Block (SMB) shares, cluster shared volumes, SANs, or local storage.

Hors, nous allons voir que le design même employé pour la création des UPDs ne permet pas l'utilisation de n'importe quel partage SMB.

Dans le cas d'une baie tierce partie (EMC ou NetApp par exemple) exposée directement en SMB, le message d'erreur suivant va se produire :
« Impossible de créer le fichier VHD du modèle. Message d'erreur : Le serveur RPC n'est pas disponible. »

En voici la cause.

Lorsque l'on utilise Server Manager pour la mise en place des UPDs, on spécifie l'emplacement du partage que l'on souhaite utiliser :

servermanagerapply

Lorsque l'on clique sur le bouton « apply », le code va devoir se connecter au partage, vérifier son existence et lui assigner les permissions adéquates.
Pour ce faire, il utilise entre autres des appels WMI via DCOM.
Le design même de la fonctionnalité interdit donc toute communication avec un hôte n'implémentant pas les interfaces WMI appelées en DCOM (pour simplifier, toute machine non Windows).
Par défaut, il faut donc héberger le partage sur une machine Windows ou un cluster Windows.

Mais comment contourner ce design et exposer une baie tierce tournant sur un OS non Microsoft (Unix, Linux etc…) ?
On peut imaginer plusieurs solutions tant que le code des User Profile Disks « discute » avec un serveur Windows…
Il est possible par exemple d'exposer les baies tierce partie via des LUNs présentées à un serveur Windows.
On peut utiliser un Clustered file server Microsoft qui permettra de s'interfacer.
Etc…

Informations techniques détaillées sur le code utilisé.
Lorsque l'on clique sur le bouton « apply » du screenshot précédent, le server manager va lancer des commandes PowerShell afin d'effectuer les actions nécessaires.
Pour ce faire, un appel à un processus wsmprovhost.exe est effectué.

En déboguant ce processus avec windbg, nous voyons que, pour effectuer les actions nécessaires, les composants suivants sont utilisés :
==
0:014> kL
# Child-SP          RetAddr           Call Site
00 0000006c`3212d8c0 00007ffb`e34b13df combase!CComActivator::DoCreateInstance+0x162
01 0000006c`3212d960 00007ffb`d6622f64 combase!CoCreateInstanceEx+0xb6
02 0000006c`3212d9c0 00007ffb`d6622eaf wbemprox!CDCOMTrans::DoActualCCI+0x50
03 (Inline Function) --------`-------- wbemprox!CDCOMTrans::DoCCI+0x17e3
04 0000006c`3212da20 00007ffb`d662236b wbemprox!CDCOMTrans::DoActualConnection+0x768
05 (Inline Function) --------`-------- wbemprox!CDCOMTrans::DoConnection+0x48
06 0000006c`3212dc50 00007ffb`da9d1c8f wbemprox!CLocator::ConnectServer+0x10b
07 0000006c`3212dcf0 00007ffb`756b7f3a wminet_utils!ConnectServerWmi+0xa7
08 0000006c`3212dd70 00007ffb`cf381cb5 system_management_ni!DomainNeutralILStubClass.IL_STUB_PInvoke(System.String, System.String, IntPtr, System.String, Int32, System.String, System.Management.IWbemContext, System.Management.IWbemServices ByRef, Int32, Int32)+0x4aa
09 0000006c`3212e010 00007ffb`cf3221ea system_management_ni!System.Management.SecuredConnectHandler.ConnectNSecureIWbemServices(System.String, System.Management.IWbemServices ByRef)+0x145
0a 0000006c`3212e0e0 00007ffb`cf321c07 system_management_ni!System.Management.ManagementScope.InitializeGuts(System.Object)+0x24a
0b 0000006c`3212e190 00007ffb`756bab65 system_management_ni!System.Management.ManagementScope.Initialize()+0x167
0c 0000006c`3212e210 00007ffb`756ba83c microsoft_remotedesktopservices_management_activities!Microsoft.RemoteDesktopServices.Management.Activities.RdFarmUtility.GetShareLocalPath(System.String, System.String)+0x155
0d 0000006c`3212e2a0 00007ffb`756b9dce microsoft_remotedesktopservices_management_activities!Microsoft.RemoteDesktopServices.Management.Activities.RdFarmUtility.AddServersToSmbStoragePermissions(System.String, System.String, System.Collections.ObjectModel.Collection`1<System.String> ByRef)+0x9c
0e 0000006c`3212e330 00007ffb`756b9ce1 microsoft_remotedesktopservices_management_activities!Microsoft.RemoteDesktopServices.Management.Activities.RDSessionHostUserDataVhdUtility.CreateUserDiskTemplate(System.String, System.String, System.String, Int64)+0xae
0f 0000006c`3212e3e0 00007ffb`bd0a56ff system_activities_ni!Microsoft.VisualBasic.Activities.VisualBasicValue`1[[System.__Canon, mscorlib]].Execute(System.Activities.CodeActivityContext)+0xffffffff`b8619a71
10 0000006c`3212e420 00007ffb`bd0a1a62 system_activities_ni!System.Activities.Runtime.ActivityExecutor.ExecuteInResolutionContext[[System.__Canon, mscorlib]](System.Activities.ActivityInstance, System.Activities.Activity`1<System.__Canon>)+0x1977f
11 0000006c`3212e4b0 00007ffb`bd08bd96 system_activities_ni!System.Activities.InArgument`1[[System.Nullable`1[[System.Boolean, mscorlib]], mscorlib]].TryPopulateValue(System.Activities.Runtime.LocationEnvironment, System.Activities.ActivityInstance, System.Activities.Runtime.ActivityExecutor)+0xffffffff`ff933892
12 0000006c`3212e510 00007ffb`bd08bc5c system_activities_ni!System.Activities.ActivityInstance.InternalTryPopulateArgumentValueOrScheduleExpression(System.Activities.RuntimeArgument, Int32, System.Activities.Runtime.ActivityExecutor, System.Collections.Generic.IDictionary`2<System.String,System.Object>, System.Activities.Location, Boolean)+0x76
13 0000006c`3212e5b0 00007ffb`bd08ba6b system_activities_ni!System.Activities.ActivityInstance.ResolveArguments(System.Activities.Runtime.ActivityExecutor, System.Collections.Generic.IDictionary`2<System.String,System.Object>, System.Activities.Location, Int32)+0xcc
14 0000006c`3212e650 00007ffb`bd08b3a0 system_activities_ni!System.Activities.Runtime.ActivityExecutor+ExecuteActivityWorkItem.ExecuteBody(System.Activities.Runtime.ActivityExecutor, System.Activities.Runtime.BookmarkManager, System.Activities.Location)+0x5b
15 0000006c`3212e6d0 00007ffb`bd08b1ff system_activities_ni!System.Activities.Runtime.ActivityExecutor.OnExecuteWorkItem(System.Activities.Runtime.WorkItem)+0x100

==

Le code .Net de microsoft.remotedesktopservices.management.activities.dll utilise system.management.dll afin d'envoyer des requêtes WMI via DCOM a l'hôte distant hébergeant le partage réseau.

Le code .Net essaie de localiser le chemin local afin d'y spécifier les permissions.
Cela se traduit par un appel DCOM vers l'interface suivante :
==
https://msdn.microsoft.com/en-us/library/cc250755.aspx
The IWbemLevel1Login interface allows a user to connect to the management services interface in a particular namespace. The interface MUST be uniquely identified by the UUID {F309AD18-D86A-11d0-A075-00C04FB68820}.
==

Si la machine hébergeant le partage n'est pas un serveur Windows (Unix ou Linux par exemple), cet appel échouera avec l'erreur « The RCP server is unavailable » :
==
0x7ffbd6622d70 : {F309AD18-D86A-11D0-A075-00C04FB68820}
0x800706ba (The RPC server is unavailable.) ==

Si on utilise la réflexion .Net pour voir le code associé, on voit ceci :
==

// Microsoft.RemoteDesktopServices.Management.Activities.RdFarmUtility
public static void AddServersToSmbStoragePermissions(string rdmsServer, string smbSharePath, ref Collection<string> fqdnServersToAllow)
{
string[] array = smbSharePath.Split(new char[]
{
'\\'
});
string shareHost = array[2];
string shareName = array[3];
if (array.Length < 4)
{
throw new Exception(string.Format(RDManagementResources.SharePathIsNotAccessible, smbSharePath));
}
string text = RdFarmUtility.GetShareLocalPath(shareHost, shareName);
bool flag = false;
if (text == null)
{
text = RdFarmUtility.GetScaleOutShareLocalPath(shareHost, shareName);
if (text != null)
{
RdFarmUtility.AddColocationToSmbPermissions(shareHost, shareName, text);
flag = true;
}
}
if (text == null)
{
throw new Exception(string.Format(RDManagementResources.SharePathIsNotAccessible, smbSharePath));
}
foreach (string current in fqdnServersToAllow)
{
RdFarmUtility.GiveServerFullControlOnSmbStorage(rdmsServer, shareHost, shareName, text, current, ref flag);
}
}

// Microsoft.RemoteDesktopServices.Management.Activities.RdFarmUtility
public static string GetShareLocalPath(string shareHost, string shareName)
{
ConnectionOptions connectionOptions = new ConnectionOptions();
connectionOptions.Authentication = AuthenticationLevel.PacketPrivacy;
connectionOptions.Impersonation = ImpersonationLevel.Impersonate;
connectionOptions.EnablePrivileges = true;
ManagementScope managementScope = new ManagementScope(string.Format(CultureInfo.InvariantCulture, "\\\\{0}\\root\\cimv2", new object[]
{
shareHost
}), connectionOptions);
managementScope.Connect();
ManagementObject firstInstance = WMIUtility.GetFirstInstance(managementScope, "Win32_Share", "Name=\"" + shareName + "\"");
if (firstInstance != null)
{
return firstInstance["Path"].ToString();
}
return null;
}
==

Ce code ne permet pas de contacter un hôte non Microsoft… Les baies tierces parties sont donc exclues… sauf si on les présente a travers une machine Windows.