Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Diversi partner offrono soluzioni di distribuzione delle applicazioni per Azure Desktop virtuale tramite l'integrazione con Collegamento app. Questo articolo fornisce collegamenti ai partner in cui è possibile leggere altre informazioni sulla connessione a desktop virtuale Azure. È anche possibile usare la soluzione di collegamento app nativa per distribuire dinamicamente le applicazioni agli host di sessione.
Recapito dell'applicazione partner
I partner seguenti hanno soluzioni di recapito delle applicazioni approvate per l'uso con Azure Desktop virtuale. Per informazioni su come distribuire applicazioni in Desktop virtuale Azure, vedere la documentazione.
| Partner | Documentazione dei partner | Supporto per i partner |
|---|---|---|
| Liquidware | Documentazione di Liquidware FlexApp | Supporto di Liquidware |
| Numecent | Documentazione di Numecent Cloudpager | Supporto numecent |
| Omnissa | Documentazione dei volumi di app Omnissa | Supporto di Omnissa |
Importante
Se si verifica un problema durante il tentativo di recapito delle applicazioni agli host sessione di Desktop virtuale Azure, è necessario verificare se è univoco per il partner approvato. È possibile verificare se si tratta di un problema univoco provando a riprodurlo nella panoramica di Collegamento app. Se non è possibile riprodurre il problema in Collegamento app di prima parte, è necessario contattare il provider del client per il supporto.
Istruzioni di integrazione di terze parti
Panoramica
Queste istruzioni riguardano due aree di integrazione:
- Piano di controllo: interazioni tra il piano di controllo di terze parti e il piano di controllo di Desktop virtuale Azure (AVD).
- Agente: interazioni tra l'agente di terze parti e l'agente AVD.
Il piano di controllo di terze parti crea oggetti proxy nel piano di controllo AVD. L'amministratore assegna quindi tali oggetti ai pool di host AVD e agli utenti. Quando un utente accede, AVD passa gli oggetti proxy che l'utente dispone dell'autorizzazione per l'uso nel pool host assegnato all'agente AVD in corrispondenza o poco prima dell'ora di accesso. L'agente AVD invia quindi tali informazioni tramite una named pipe all'agente di terze parti, rendendo le app disponibili per l'utente.
Flusso del piano di controllo
Prerequisiti
Il piano di controllo di terze parti deve fornire all'amministratore un modo per autorizzarlo a creare e leggere gli oggetti pacchetto di collegamento dell'app in AVD per loro conto. Le opzioni includono un'entità servizio, Azure Lighthouse o altri metodi di autorizzazione. Per concedere al piano di controllo di terze parti le autorizzazioni per effettuare chiamate CRUD per i pacchetti di collegamento di app, assegnare il ruolo controllo degli accessi in base al ruolo predefinito Desktop Virtualization App Attach Contributor all'entità servizio di terze parti. Anche la terza parte deve avere un'offerta nel Marketplace Azure.
Creazione di pacchetti
La terza parte chiama il pacchetto di collegamento app AVD - Creare o aggiornare l'API REST per creare un oggetto AppAttachPackage. Il nome impostato nella chiamata API REST non può essere modificato e deve essere univoco all'interno di un gruppo di risorse. Compilare i campi dell'oggetto come indicato di seguito:
| Dati archiviati del pacchetto di collegamento dell'app | Campo di terze parti equivalente |
|---|---|
| DisplayName [filtrabile] | Nome alternativo visibile all'amministratore nel portale di Azure |
| PackageRelativePath [filtrabile] | Percorso dell'eseguibile per l'esecuzione di applicazioni per le app remote |
| Percorso dell'eseguibile per l'esecuzione di applicazioni per le app remote | Determina se un pacchetto è pronto per l'uso |
| Versione | Apri per l'uso |
| Lastupdated | Il pacchetto data è stato aggiornato per ultimo da terze parti (Azure data dell'ultimo aggiornamento predefinita potrebbe essere modificata dall'amministratore che aggiunge un hostpool al pacchetto) |
| FailHealthCheckOnStagingFailure | Indica se un problema durante la preparazione del pacchetto deve non riuscire durante il controllo dell'integrità |
| KeyVaultURL | Apri per l'uso |
| ImagePath [filtrabile] | Apri per l'uso |
| PackageFullName [filtrabile] | Apri per l'uso |
| PackageName [filtrabile] | Apri per l'uso |
| PackageFamilyName [filtrabile] | Questo campo più l'appid nel campo dell'oggetto applicazione devono identificare in modo univoco un'app per un utente (necessario per la creazione remota dell'app) |
| PackageApplications | Utilizzato da terze parti per lo stesso scopo |
| PackageApplications:AppUserModelId | Argomenti della riga di comando per eseguire l'applicazione come app remota |
| PackageApplications:AppId | In combinazione con il nome della famiglia di pacchetti, deve identificare in modo univoco un'app |
| PackageApplications:Description | Descrizione dell'applicazione |
| PackageApplications:FriendlyName | Nome descrittivo dell'applicazione |
| PackageApplications:IconImageName | Nome del file di icona |
| PackageApplications:RawIcon | Non richiesto |
| PackageApplications:RawPng | Questa operazione può essere creata usando il frammento di codice seguente |
| PackageDependencies | Questi dati saranno visibili all'amministratore, ma AVD non userà queste informazioni |
| PackageDependencies:DependencyName | Nome della dipendenza |
| PackageDependencies:Publisher | Autore della dipendenza |
| PackageDependencies:MinVersion | Versione della dipendenza |
| IsRegularRegistration [filtrabile] | Apri per l'uso |
| HostPoolReferences [filtrabile] | Ancora usato da AVD |
| CertificateExpiryDate | Apri per l'uso |
| CertificateName [filtrabile] | Apri per l'uso |
| PackageOwnerName [filtrabile] | Nome di terze parti |
| PackageLookbackUrl | URL del piano di controllo di terze parti in cui è archiviato il pacchetto |
| CustomData [filtrabile] | Campo in cui è possibile archiviare dati personalizzati |
Il valore per i RawPng campi negli oggetti applicazione può essere estratto da un file di icona usando il codice seguente:
using (MemoryStream iconInStream = new MemoryStream(iconByteArray))
{
using (Icon ic = new Icon(iconInStream, iconSize, iconSize))
{
using (Bitmap bitmap = ic.ToBitmap())
{
using (MemoryStream pngOutStream = new MemoryStream())
{
bitmap.Save(pngOutStream, System.Drawing.Imaging.ImageFormat.Png);
pngOutStream.Close();
return new PngIcon(bitmap.Height, bitmap.Width, pngOutStream.ToArray());
}
}
}
}
Se un pacchetto con tutti i PNG non elaborati sarebbe troppo grande, creare le applicazioni con PNG Null. Aggiornare quindi le applicazioni una alla volta chiamando l'API di aggiornamento con una singola immagine dell'applicazione. Ripetere fino a quando non vengono popolate tutte le immagini.
Mantenere aggiornati i pacchetti
Le terze parti sono responsabili di mantenere sincronizzati i pacchetti nel piano di controllo AVD con i pacchetti nel piano di controllo di terze parti. Usare App Attach Package - Update REST API (Pacchetto di collegamento app - Aggiorna API REST ) per aggiornare i pacchetti. I campi sono uguali all'API di creazione.
App remote
Un amministratore può creare app remote che puntano a pacchetti di collegamento di app di terze parti. L'amministratore specifica il nome della famiglia di pacchetti e l'ID app nell'app remota. Questo approccio consente all'amministratore o a terze parti di eseguire l'aggiornamento a una nuova versione senza modificare il feed. Quando l'utente seleziona l'app nel feed, l'eseguibile specificato nel PackageRelativePath campo viene eseguito nel computer con i parametri specificati nel AppUserModelId campo per l'applicazione scelta.
Flusso dell'agente
Prerequisiti
Un amministratore installa l'agente di terze parti nell'immagine.
Panoramica
Quando AVD sceglie l'host a cui l'utente accede (orchestrazione), AVD scriverà una mappa tra gli utenti e i pacchetti in una named pipe come CMS firmato. L'agente di terze parti sarà il client named pipe e l'agente di AVD sarà il server. All'avvio dell'agente di terze parti, invierà un messaggio all'agente di AVD con il nome del proprietario del pacchetto e un identificatore specifico dell'host. L'identificatore specifico dell'host viene usato per impedire attacchi di riproduzione. Un attore non valido può usare il messaggio entitlements e riprodurlo in un altro host, consentendogli di ignorare i diritti a livello di pool di host. AVD includerà questo identificatore nel CMS firmato quando comunica i diritti all'agente di terze parti. AVD invierà quindi diritti durante l'orchestrazione, che si verifica poco prima dell'accesso. La mappa inviata indicherà il diritto dei pacchetti al momento dell'accesso dell'utente, ma invitiamo le terze parti a riconvalidare anche questo con Azure controllo degli accessi in base al ruolo e i riferimenti al pool host nell'oggetto Pacchetto di collegamento app se vogliono apportare modifiche al pacchetto durante la sessione utente.
Avvio
In primo luogo, l'agente di terze parti determina se si tratta di uno scenario AVD controllando questi due elementi:
Un processo denominato
RDAgentBootLoader.exeè in esecuzione nel computer. Questo servizio viene eseguito come SYSTEM nell'host della sessione e avvia il server named pipe all'avvio.
Presenza di questa chiave del Registro di sistema:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\RDAgentBootLoader
L'agente di terze parti invia un messaggio all'agente AVD tramite la named pipe. Il messaggio identifica l'agente in base al nome del proprietario del pacchetto specificato negli oggetti del pacchetto creati dalla terza parte.
a) Nome pipe: "AppAttachThirdPartyServer_Pipe"
b) Connessione a named pipe: metodo NamedPipeClientStream.ConnectAsync (System.IO.Pipes) | Microsoft Learn
for (int attemptNumber = 1; attemptNumber <= _numberOfAttempts; attemptNumber++)
{
try
{
_logger.LogInformation($"Connecting to Named pipe server - Attempt: #{attemptNumber}");
await ConnectAsync(_pipe, _connectTimeoutMilliseconds, cancellationToken).ConfigureAwait(false);
}
catch (TimeoutException)
{
if (attemptNumber >= _numberOfAttempts)
{
_logger.LogError($"Failed to connect to the named pipe server in {_numberOfAttempts} attempts.");
throw;
}
continue;
}
catch (Exception ex)
{
_logger.LogError($"Exception occurred: {ex}");
}
break;
}
Formato del messaggio inviato da terze parti ad AVD durante l'avvio (in formato JSON).
Usare il metodo PipeStream.WriteAsync (System.IO.Pipes) | Microsoft Learn per scrivere dati dal flusso client named pipe.
{
"PackageOwnerName": "packageOwnerName",
"HostIdentifier": "hostIdentifier",
"Version": "version"
}
PackageOwnerName : deve corrispondere al campo nell'oggetto PackageOwnerName pacchetto di collegamento dell'app.
HostIdentifier : identificatore (massimo 100 caratteri) che identifica in modo univoco l'host. Usato per evitare attacchi di riproduzione.
Version : versione del protocollo di comunicazione, attualmente 1.
Flusso di accesso e orchestrazione utente
Quando un utente accede, l'agente AVD invia un messaggio alla named pipe come cms firmato dal certificato RDP AVD per specificare i pacchetti che l'utente può usare. Se l'utente usa un'app remota, l'app selezionata viene visualizzata per prima nell'elenco. La terza parte deve rendere disponibili tutte le applicazioni incluse nel messaggio perché un utente può aprire più app remote nella stessa sessione. Se l'utente non dispone di autorizzazioni per l'app, AVD invia un elenco vuoto.
{
"createdData": "date orchestration request was sent, older requests should be ignored",
"scenario": "Remote", // can be “Remote” or “Desktop” depending on if the user invoked a remote app or using the session desktop
“Version”: 1, // version of the data that AVD sends to third parties. We’ll increment this in case anything changes and communicate accordingly.
"pageNumber": 1,
"totalPages": 1,
HostIdentifier”: “<hostidentifier>” // third party agent sends this to AVD agent during bootup. We relay that information to you so a replay attack can be prevented.
"PackageSignature": “<signed cms byte array>”
}
Attualmente e pageNumbertotalPages sono statici perché il limite di 64 KB per le named pipe si applica solo alle versioni precedenti di Windows.
PackageSignature è la matrice di byte CMS firmata. Quando decodificato, ha il formato seguente:
{
"UPN": "user@domain.com",
"HostIdentifier": "hostidentifier",
"Packages": [
{
"OnDemandRegister": false,
"FailHealthCheckOnStagingFailure": 1,
"FamilyName": "Mozilla.MozillaFirefox_gmpnhwe7bv6081",
"PackageName": "Mozilla.MozillaFirefox1",
"PackageOwnerName": "packageownername",
"CustomData": "",
"ImagePath": ""
},
{
"OnDemandRegister": false,
"FailHealthCheckOnStagingFailure": 1,
"FamilyName": "b0413c83-dbce-420e-8905-4444",
"PackageName": "PuTTY",
"PackageOwnerName": "packageownername",
"CustomData": "",
"ImagePath": ""
}
]
}
La terza parte può convalidare e decodificare la matrice di byte CMS firmata con il codice seguente:
// a) Validate certificate
// Build certificate from response
X509Certificate2 cert = new X509Certificate2(System.Text.Encoding.UTF8.GetBytes(payload), new SecureString(), X509KeyStorageFlags.EphemeralKeySet);
// Build certificate chain
X509Chain chain = new X509Chain();
chain.Build(cert);
foreach (X509ChainElement element in chain.ChainElements)
{
// Here you could validate that the issuer is AVD.
logger.LogInformation($"{funcName}: Element issuer: {element.Certificate.Issuer};");
logger.LogInformation($"{funcName}: Element subject: {element.Certificate.Subject}");
logger.LogInformation($"{funcName}: Element certificate valid until: {element.Certificate.NotAfter}");
logger.LogInformation($"{funcName}: Element certificate is valid: {element.Certificate.Verify()}");
if (!element.Certificate.Verify())
{
return false;
}
}
// b) Decode the encoded bytes only if the certificate is valid
byte[] blob = Convert.FromBase64String(payload);
SignedCms signedCms = new SignedCms();
signedCms.Decode(blob);
string result = Encoding.UTF8.GetString(signedCms.ContentInfo.Content);
EntitlementJSONData data = JsonConvert.DeserializeObject<PackagesJSONData>(result);
DateTime utcNow = DateTime.UtcNow;
Telemetria
La soluzione App Attach di prima parte segnala errori di registrazione e checkpoint nel contesto di una connessione. Questa segnalazione consente agli amministratori di tenere traccia degli errori di connessione causati da un problema con Collegamento app. Le app di terze parti devono segnalare gli errori nello stesso modo in modo che gli amministratori abbiano visibilità sugli errori.
Le terze parti segnalano i tipi di evento seguenti:
Chiamata all'app
Uscita dall'app
Esito positivo o negativo dell'installazione (registrazione)
Le terze parti usano La registrazione traccia eventi (ETL) per scrivere eventi con la macro TraceLoggingWriteActivity. AVD usa tali eventi e invia informazioni di diagnostica nel contesto della connessione dell'utente a cui l'amministratore può accedere.
Informazioni sul provider ETW di terze parti
La terza parte fornisce il nome e il GUID del provider ETW nel percorso del Registro di sistema seguente all'avvio. Quando l'agente di terze parti viene installato, crea questo percorso del Registro di sistema se non esiste e aggiunge informazioni sul provider come coppia chiave-valore. L'agente di AVD gestisce i callback sugli eventi generati dai provider ETW di terze parti.
Per impostazione predefinita, AVD non usa i dati di telemetria dell'agente di terze parti. AVD richiede il nome del provider e il GUID del provider di terze parti in modo che possa essere testato prima della distribuzione nell'ambiente di produzione.
Informazioni segnalate da terze parti tramite eventi ETL
| Informazioni necessarie | Descrizione | Obbligatorio |
|---|---|---|
| Sessionid | ID sessione dell'utente. AVD deve eseguire la ricerca inversa dell'ID attività per la diagnostica. | Sì |
| Nome evento | I parser di AVD gestiscono i callback su questi eventi dal provider di terze parti. | Sì |
| Parametri | Vedere di seguito per ogni tipo di evento. | Sì |
Errore di registrazione
Il messaggio di errore deve essere interattivo per l'amministratore.
Il codice di errore deve essere documentato nella documentazione di terze parti in modo che gli amministratori possano diagnosticare il problema.
TraceLoggingWriteActivity( hProvider, "Error", "{AnyGuid}", // Use Guid.NewGuid() to generate this NULL, // The related activity ID for the event, or NULL for no related activity ID. TraceLoggingString("SessionID", "SessionID"), TraceLoggingString("RegistrationFailure", "Operation "), TraceLoggingString("{ThirdPartyName}", "Source"), TraceLoggingString("{PackageName}", "PackageName"), TraceLoggingString({errorMessage}, "ErrorMessage"), TraceLoggingUInt32({code}, "ErrorCode"), TraceLoggingBool({InternalOrExternal}, "IsInternalErrorCode"), )Registrazione completata
TraceLoggingWriteActivity( hProvider, "Checkpoint", "{AnyGuid}", // Use Guid.NewGuid() to generate this NULL, // The related activity ID for the event, or NULL for no related activity ID. TraceLoggingString("SessionID", "SessionID"), TraceLoggingString("RegistrationSuccess", "Operation"), TraceLoggingString("{ThirdPartyName}", "Source"), TraceLoggingString("{PackageName}", "PackageName"), );Chiamata all'app
TraceLoggingWriteActivity( hProvider, "Checkpoint", "{AnyGuid}", // Use Guid.NewGuid() to generate this NULL, // The related activity ID for the event, or NULL for no related activity ID. TraceLoggingString("SessionID", "SessionID"), TraceLoggingString("RegistrationSuccess", "Operation"), TraceLoggingString("{ThirdPartyName}", "Source"), TraceLoggingString("{PackageName}", "PackageName"), );Uscita dall'app
TraceLoggingWriteActivity( hProvider, "Checkpoint", "{AnyGuid}", // Use Guid.NewGuid() to generate this NULL, // The related activity ID for the event, or NULL for no related activity ID. TraceLoggingString("SessionID", "SessionID"), TraceLoggingString("AppExit", "Operation"), TraceLoggingString("{ThirdPartyName}", "Source"), TraceLoggingString("{AppName}", "AppName"), );
Passaggi successivi
Per altre informazioni sui client Desktop remoto, vedere Panoramica di Collegamento app.