import type { WebSiteManagementModels } from '@azure/arm-appservice' ; import { Environment } from '@azure/ms-rest-azure-env' ; import { BlobSASPermissions , BlobServiceClient , BlockBlobClient , ContainerClient , generateBlobSASQueryParameters , StorageSharedKeyCredential } from '@azure/storage-blob' ; import * as dayjs from 'dayjs' ; import * as relativeTime from 'dayjs/plugin/relativeTime' ; import * as utc from 'dayjs/plugin/utc' ; import { URL } from 'url' ; import { IActionContext , parseError } from 'vscode-azureextensionui' ; import { ext } from '../extensionVariables' ; import { localize } from '../localize' ; import { ParsedSite } from '../SiteClient' ; import { randomUtils } from '../utils/randomUtils' ; import { IDeployContext } from './IDeployContext' ; import { runWithZipStream } from './runWithZipStream' ; dayjs . extend ( relativeTime ) ; dayjs . extend ( utc ) ; /**
 * Method of deployment that is only intended to be used for Linux Consumption Function apps because it doesn't support kudu pushDeployment
 * To deploy with Run from Package on a Windows plan, create the app setting "WEBSITE_RUN_FROM_PACKAGE" and set it to "1".
 * Then deploy via "zipdeploy" as usual.
 */ export async function deployToStorageAccount ( context , fsPath , site ) { context . telemetry . properties . useStorageAccountDeploy = 'true' ; const datePart = dayjs ( ) . utc ( ) . format ( 'YYYYMMDDHHmmss' ) ; const randomPart = randomUtils . getRandomHexString ( 32 ) ; const blobName = `${ datePart }-${ randomPart }.zip` ; const blobService = await createBlobServiceClient ( context , site ) ; const blobUrl = await createBlobFromZip ( context , fsPath , site , blobService , blobName ) ; const client = await site . createClient ( context ) ; const appSettings = await client . listApplicationSettings ( ) ; appSettings . properties = appSettings . properties || { } ; delete appSettings . properties . WEBSITE_RUN_FROM_ZIP ; appSettings . properties . WEBSITE_RUN_FROM_PACKAGE = blobUrl ; await client . updateApplicationSettings ( appSettings ) ; ext . outputChannel . appendLog ( localize ( 'deploymentSuccessful' , 'Deployment successful.' ) , { resourceName : site . fullName } ) ; context . syncTriggersPostDeploy = true ; } async function createBlobServiceClient ( context , site ) { const client = await site . createClient ( context ) ; const azureWebJobsStorageKey = 'AzureWebJobsStorage' ; const settings = await client . listApplicationSettings ( ) ; let connectionString = settings . properties && settings . properties [ azureWebJobsStorageKey ] ; if ( connectionString ) { try { return BlobServiceClient . fromConnectionString ( connectionString ) ; } catch ( error ) { const endpointSuffix = 'EndpointSuffix' ; const separator = ';' ; if ( parseError ( error ) . message . includes ( endpointSuffix ) && ! connectionString . includes ( endpointSuffix ) ) { if ( ! connectionString . endsWith ( separator ) ) { connectionString += separator ; } connectionString += `${ endpointSuffix }=${ Environment . AzureCloud . storageEndpointSuffix }${ separator }` ; return BlobServiceClient . fromConnectionString ( connectionString ) ; } else { throw error ; } } } else { throw new Error ( localize ( 'azureWebJobsStorageKey' , '"{0}" app setting is required for Run From Package deployment.' , azureWebJobsStorageKey ) ) ; } } async function createBlobFromZip ( context , fsPath , site , blobService , blobName ) { const containerName = 'function-releases' ; const containerClient = blobService . getContainerClient ( containerName ) ; if ( ! await containerClient . exists ( ) ) { await containerClient . create ( ) ; } const blobClient = containerClient . getBlockBlobClient ( blobName ) ; await runWithZipStream ( context , { fsPath , site , callback : async ( zipStream ) => { ext . outputChannel . appendLog ( localize ( 'creatingBlob' , 'Uploading zip package to storage container...' ) , { resourceName : site . fullName } ) ; await blobClient . uploadStream ( zipStream ) ; } } ) ; void blobClient . getProperties ( ) . then ( r => { context . telemetry . measurements . blobSize = Number ( r . contentLength ) ; } ) ; if ( blobService . credential instanceof StorageSharedKeyCredential ) { const url = new URL ( blobClient . url ) ; url . search = generateBlobSASQueryParameters ( { containerName , blobName , permissions : BlobSASPermissions . parse ( 'r' ) , startsOn : dayjs ( ) . utc ( ) . subtract ( 5 , 'minute' ) . toDate ( ) , expiresOn : dayjs ( ) . utc ( ) . add ( 10 , 'year' ) . toDate ( ) } , blobService . credential ) . toString ( ) ; return url . toString ( ) ; } else { throw new Error ( 'Internal Error: Expected credential to be of type "StorageSharedKeyCredential".' ) ; } }