/// <summary>
        /// Copies asset data from source Azure Media Services instance to target using Azure Blob Copy
        /// </summary>
        /// <param name="sourceClient">Source Azure Media Services instance client</param>
        /// <param name="sourceConfig">Source Azure Media Services instance configuration</param>
        /// <param name="targetClient">Target Azure Media Services instance client</param>
        /// <param name="targetConfig">Target Azure Media Services instance configuration</param>
        /// <param name="provisioningRequest">Provisioning request data</param>
        /// <param name="logger">Logger to log data</param>
        /// <returns>Target asset</returns>
        private async Task <Asset> CopyAssetAsync(IAzureMediaServicesClient sourceClient, MediaServiceConfigurationModel sourceConfig,
                                                  IAzureMediaServicesClient targetClient, MediaServiceConfigurationModel targetConfig,
                                                  ProvisioningRequestModel provisioningRequest, ILogger logger)
        {
            logger.LogInformation($"AssetDataProvisioningService::CopyAssetAsync started: provisioningRequest={LogHelper.FormatObjectForLog(provisioningRequest)} sourceInstanceName={sourceConfig.AccountName} targetInstanceName={targetConfig.AccountName}");

            // Need to ensure that target asset exits
            var targetAsset = await targetClient.Assets.GetAsync(targetConfig.ResourceGroup, targetConfig.AccountName, provisioningRequest.ProcessedAssetName).ConfigureAwait(false);

            // if there is no target asset, need to provision one
            if (targetAsset == null)
            {
                // create new target asset
                targetAsset = await targetClient.Assets.CreateOrUpdateAsync(targetConfig.ResourceGroup, targetConfig.AccountName, provisioningRequest.ProcessedAssetName, new Asset()).ConfigureAwait(false);

                // need to reload asset to get Container value populated, otherwise Container is null after asset creation
                targetAsset = await targetClient.Assets.GetAsync(targetConfig.ResourceGroup, targetConfig.AccountName, provisioningRequest.ProcessedAssetName).ConfigureAwait(false);
            }

            // Get SAS token associated with source asset. SAS token is required to initiate StartCopyFromUri
            var sourceAssetContainerSas = await sourceClient.Assets.ListContainerSasAsync(
                sourceConfig.ResourceGroup,
                sourceConfig.AccountName,
                provisioningRequest.ProcessedAssetName,
                permissions : AssetContainerPermission.Read,
                expiryTime : DateTime.UtcNow.AddHours(1).ToUniversalTime()).ConfigureAwait(false);

            var sourceContainerSasUrl = new Uri(sourceAssetContainerSas.AssetContainerSasUrls.FirstOrDefault());

            var sourceBlobClient = new BlobContainerClient(sourceContainerSasUrl);

            var copyTasks = new List <Task>();

            // Get a list of all blobs to copy
            await foreach (var blobItem in sourceBlobClient.GetBlobsAsync())
            {
                // All blobs can be copies in parallel
                copyTasks.Add(Task.Run(() =>
                {
                    // Get target blob
                    var targetBlob = new BlobBaseClient(this.configService.MediaServiceInstanceStorageAccountConnectionStrings[targetConfig.AccountName], targetAsset.Container, blobItem.Name);
                    // Get source blob
                    var sourceBlob = sourceBlobClient.GetBlobClient(blobItem.Name);
                    // Start copy operation, see more data about it https://docs.microsoft.com/en-us/dotnet/api/azure.storage.blobs.specialized.blobbaseclient.startcopyfromuriasync?view=azure-dotnet
                    var copyOperation = targetBlob.StartCopyFromUri(sourceBlob.Uri);
                    // Wait for copy to complete, since this is running on separate thread, no need to do async
                    var copyResult = copyOperation.WaitForCompletionAsync().GetAwaiter().GetResult();
                    // Check copy operation status
                    if (copyResult.GetRawResponse().Status != 200)
                    {
                        throw new Exception($"Copy operation failed, sourceAccount={sourceConfig.AccountName} targetAccount={targetConfig.AccountName} assetName={provisioningRequest.ProcessedAssetName} blobName={blobItem.Name} httpStatus={copyResult.GetRawResponse().Status}");
                    }
                }));
            }

            // Wait for all copy tasks to finish
            await Task.WhenAll(copyTasks).ConfigureAwait(false);

            logger.LogInformation($"AssetDataProvisioningService::CopyAssetAsync completed: provisioningRequest={LogHelper.FormatObjectForLog(provisioningRequest)} sourceInstanceName={sourceConfig.AccountName} targetInstanceName={targetConfig.AccountName} numberOfFiles={copyTasks.Count}");

            return(targetAsset);
        }