Distribuire applicazioni da soluzioni partner a desktop virtuale Azure con Collegamento app

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

Diagramma che mostra il flusso di comunicazione dell'agente tra l'agente AVD e l'agente di terze parti

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:

  1. 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.

    Screenshot di Gestione attività che mostra il processo RDAgentBootLoader in esecuzione nell'host della sessione.

  2. 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:

  1. Chiamata all'app

  2. Uscita dall'app

  3. 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.

Screenshot dell'editor del Registro di sistema che mostra il percorso del Registro di sistema del provider ETW e le coppie di valori di chiave.

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.
Nome evento I parser di AVD gestiscono i callback su questi eventi dal provider di terze parti.
Parametri Vedere di seguito per ogni tipo di evento.
  1. 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"),
    )
    
  2. 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"),
        );
    
  3. 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"),
        );
    
  4. 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.