代码之家  ›  专栏  ›  技术社区  ›  eddiewould

Azure WebApp-版本控制的配置/用户数据与应用程序分开部署

  •  1
  • eddiewould  · 技术社区  · 7 年前

    我已经开发了一个WebApp(API),它托管在Azure&将VST用于CI/CD/版本控制。

    我希望此API的客户(所有者)能够在wwwroot下更新各种配置/数据文件,但我希望这些文件处于版本控制之下(真实来源-API源代码的单独存储库)。在存储库中创建/更新/删除其中一个文件会导致在WebApp中上载/删除该文件(在wwwroot下的文件夹中)。

    修改(创建/删除)其中一个文件应 触发(WebApp)的完全重新部署

    我怎样才能做到这一点?到目前为止,我一直在考虑GIT artefact的VSTS发布管道,但我看不到在Azure webapp中进行更改的低摩擦方式(KUDU API似乎有点复杂和苛刻)

    **编辑:**示例PowerShell脚本,用于在WebApp中同步配置文件,但不包含生成构件(仅在必要时调用PUT/DELETE)。

    VSTS Configuration

    # The idea behind this script is to synchronize the configuration files on the server with what's in the repo, only updating files where necessary
    
    param (
        [string]$resourceGroupName = "XXX",
        [Parameter(Mandatory=$true)][string]$webAppName,
        [Parameter(Mandatory=$true)][string]$latestConfigFilesPath
    )
    
    function Get-AzureRmWebAppPublishingCredentials($resourceGroupName, $webAppName, $slotName = $null) {
        if ([string]::IsNullOrWhiteSpace($slotName)) {
            $resourceType = "Microsoft.Web/sites/config"
            $resourceName = "$webAppName/publishingcredentials"
        } else {
            $resourceType = "Microsoft.Web/sites/slots/config"
            $resourceName = "$webAppName/$slotName/publishingcredentials"
        }
    
        $publishingCredentials = Invoke-AzureRmResourceAction -ResourceGroupName $resourceGroupName -ResourceType $resourceType -ResourceName $resourceName -Action list -ApiVersion 2015-08-01 -Force
    
        return $publishingCredentials
    }
    
    function Get-KuduApiAuthorisationHeaderValue($resourceGroupName, $webAppName, $slotName = $null) {
        $publishingCredentials = Get-AzureRmWebAppPublishingCredentials $resourceGroupName $webAppName $slotName
        return ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $publishingCredentials.Properties.PublishingUserName, $publishingCredentials.Properties.PublishingPassword))))
    }
    
    function Get-KuduInode($kuduHref) {
        return Invoke-RestMethod -Uri $kuduHref `
                                 -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} `
                                 -Method GET `
                                 -ContentType "application/json"
    }
    
    function Get-AllFilesUnderKuduHref($kuduHref) {
        $result = @()
        $inodes = (Get-KuduInode $kuduHref)
        Foreach ($inode in $inodes) {
            if ($inode.mime -eq "inode/directory") {
                $result += (Get-AllFilesUnderKuduHref $inode.href)
            } else {
                $result += $inode.href
            }
        }
    
        return $result
    }
    
    function Get-LocalPathForUri([System.Uri]$uri) {
        $latestConfigFilesUri = [System.Uri]$latestConfigFilesPath
        $localFileUri = [System.Uri]::new($latestConfigFilesUri, $uri)
        return $localFileUri.LocalPath
    }
    
    function Get-RemoteUri([System.Uri]$uri) {
        return  [System.Uri]::new($configurationHref, $uri)
    }
    
    function Files-Identical($uri) {
        $localFilePath = Get-LocalPathForUri $uri
        $localFileHash = Get-FileHash $localFilePath -Algorithm MD5
    
        # Download the remote file so that we can calculate the hash. It doesn't matter that it doesn't get cleaned up, this is running on a temporary build server anyway.
        $temporaryFilePath = "downloded_kudu_file"
        $remoteFileUri = [System.Uri]::new($configurationHref, $uri)
        Invoke-RestMethod -Uri $remoteFileUri `
                          -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} `
                          -Method GET `
                          -OutFile $temporaryFilePath `
                          -ContentType "multipart/form-data"
    
        $remoteFileHash = Get-FileHash $temporaryFilePath -Algorithm MD5
    
        return $remoteFileHash.Hash -eq $localFileHash.Hash
    }
    
    function CalculateRelativePath([System.Uri]$needle, [System.Uri]$haystack) {
        return $haystack.MakeRelativeUri($needle).ToString();
    }
    
    function Put-File([System.Uri]$uri) {
        Write-Host "Uploading file $uri"
        $localFilePath = Get-LocalPathForUri $uri
        $remoteFileUri = Get-RemoteUri $uri
        Invoke-RestMethod -Uri $remoteFileUri `
                          -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} `
                          -Method PUT `
                          -InFile $localFilePath `
                          -ContentType "multipart/form-data"
    }
    
    function Delete-File([System.Uri]$uri) {
        Write-Host "Deleting file $uri"
        $remoteFileUri = Get-RemoteUri $uri
        Invoke-RestMethod -Uri $remoteFileUri `
                          -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} `
                          -Method DELETE `
    }
    
    # Script begins here
    
    $configurationHref = [System.Uri]"https://$webAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/Configuration/"
    $kuduApiAuthorisationToken = Get-KuduApiAuthorisationHeaderValue -resourceGroupName $resourceGroupName -webAppName $webAppName
    $filenamesOnServer = Get-AllFilesUnderKuduHref $configurationHref $kuduApiAuthorisationToken | % { $configurationHref.MakeRelativeUri($_).OriginalString }
    
    Write-Host "Files currently on server" $filenamesOnServer
    
    $filesCurrentlyInRepo = Get-ChildItem -Path $latestConfigFilesPath -Recurse -File
    $filenamesInRepo = $filesCurrentlyInRepo | Select-Object -ExpandProperty FullName | % { CalculateRelativePath $_ $latestConfigFilesPath}
    
    Write-Host "Files currently in repo" $filenamesInRepo
    
    $intersection = $filenamesOnServer | ?{$filenamesInRepo -contains $_}
    Write-Host "Intersection: " $intersection
    
    $onlyOnServer = $filenamesOnServer | ?{-Not($filenamesInRepo -contains $_)}
    $onlyInRepo = $filenamesInRepo | ?{-Not($filenamesOnServer -contains $_)}
    Write-Host "Only on server" $onlyOnServer
    Write-Host "Only in repo" $onlyInRepo
    Write-Host
    
    Foreach ($uri in $onlyInRepo) {
        Put-File $uri
    }
    
    Foreach ($uri in $onlyOnServer) {
        Delete-File $uri
    }
    
    Foreach ($uri in $intersection) {
        if (-Not (Files-Identical $uri)) {
            Write-Host "Configuration file $uri needs updating"
            Put-File $uri
        } else {
            Write-Host "Configuration file $uri is identical, skipping"
        }
    }
    
    Write-Host "Sync complete"
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   starian chen-MSFT    7 年前

    使用Azure App Service部署任务,您可以将文件上载到App Service,但不能删除文件(取消选中“使用Web部署发布”选项,并在包或文件夹输入框中指定文件夹路径)。

    因此,更好的方法是使用 Kudu API 在生成/发布期间删除/上载文件。

    有一个关于使用Kudu API的帖子和博客:

    How to access Kudu in Azure using power shell script

    Interacting with Azure Web Apps Virtual File System using PowerShell and the Kudu API

    推荐文章