Developments in Digital
Developments in Digital

Deploying an Azure static website through PowerShell

One option for hosting a static website within Azure is to use the 'Static Website' feature of a storage container.

To create this infrastructure in PowerShell, you can use the Azure REST API to enable the Static Website option within an existing storage container.

function Set-StorageAccountEnableStaticWebsite {
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    param(
        [Parameter(Mandatory = $true)] $StorageContext,
        [Parameter()][string] $IndexDocument = "index.html",
        [Parameter()][string] $ErrorDocument404Path = "404.html"
    )

    $token = New-AzureStorageAccountSASToken `
        -Service Blob, File, Table, Queue `
        -ResourceType Service, Container, Object `
        -Permission "racwdlup" `
        -Context $storageAccount.Context

    $storageAccountName = $storageAccount.StorageAccountName

    $uri = "https://$storageAccountName.blob.core.windows.net"
    $uri = $uri + $token
    $uri = $uri + "&restype=service&comp=properties"
    $uri = $uri + "&api-version=2018-03-28"

    $body = @"
<?xml version="1.0" encoding="utf-8"?>
<StorageServiceProperties>
<StaticWebsite>
<Enabled>true</Enabled>
<IndexDocument>$IndexDocument</IndexDocument>
<ErrorDocument404Path>$ErrorDocument404Path</ErrorDocument404Path>
</StaticWebsite>
</StorageServiceProperties>
"@

    Invoke-RestMethod -Uri $uri -Method Put -Body $body
}

Files can be uploaded to the configured storage container:

function Set-BlobContentFromFolderRecursive {
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    param(
        [Parameter(Mandatory = $true)] $StorageContext,
        [Parameter(Mandatory = $true)][string] $ContainerName,
        [Parameter(Mandatory = $true)][string] $Folder,
        [Parameter()][boolean] $RemoveAll = $false,
        [Parameter()][boolean] $SetDefaultContentTypes = $false
    )

    If ($RemoveAll -eq $true) {
        $blobs = Get-AzureStorageBlob -Context $StorageContext -Container $ContainerName
        $blobs | Remove-AzureStorageBlob
    }

    $container = Get-AzureStorageContainer -Name $ContainerName -Context $StorageContext

    $AbsoluteFolder = Resolve-Path -Path $Folder
    if ($container) {
        $filesToUpload = Get-ChildItem $AbsoluteFolder.Path -Recurse -File

        foreach ($fileInfo in $filesToUpload) {
            $filePath = $fileInfo.fullname
            $targetPath = $filePath.Substring($AbsoluteFolder.Path.Length + 1).Replace("\", "/")

            $fileUri = $($container.CloudBlobContainer.Uri.AbsoluteUri + "/" + $targetPath)

            Set-AzureStorageBlobContent `
                -File $filePath `
                -Container $container.Name `
                -Blob $targetPath `
                -Context $StorageContext `
                -Force:$true
        }

        If ($SetDefaultContentTypes -eq $true) {
            Set-BlobContentType `
                -StorageContext $StorageContext `
                -ContainerName $ContainerName
        }

        return
    }

    throw "Container not found '$ContainerName'"
}

function Set-BlobContentType {
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    param(
        [Parameter(Mandatory = $true)] $StorageContext,
        [Parameter(Mandatory = $true)][string] $ContainerName
    )

    $blobs = Get-AzureStorageBlob -Context $StorageContext -Container $ContainerName

    foreach ($blob in $blobs) {
        $extension = [IO.Path]::GetExtension($Blob.Name)
        $contentType = ""

        switch ($extension) {
            ".html" { $contentType = "text/html" }
            ".json" { $contentType = "application/json" }
            ".js" { $contentType = "application/javascript" }
            ".svg" { $contentType = "image/svg+xml" }
            Default { $contentType = "" }
        }

        if ($contentType -ne "") {
            $cloudBlockBlob = [Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob] $Blob.ICloudBlob

            $cloudBlockBlob.Properties.ContentType = $ContentType
            $cloudBlockBlob.SetProperties()
        }
    }
}