Azure Static Website
ADD
azure-ts-static-website/Pulumi.yaml
+name: azure-ts-static-website
+runtime: nodejs
+description: An example of a static website hosted on Azure Blob Storage + Azure CDN.

ADD
azure-ts-static-website/README.md
+[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new)
+
+# A Static Website Hosted on Azure Blob Storage + Azure CDN
+
+This example configures [Static website hosting in Azure Storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website). One complication is the fact that the Static Website feature of Storage Accounts is not part of Azure Resource Manager, and is not configurable directly via Pulumi Azure provider.
+
+As a workaround, a custom dynamic provider and a dynamic resource are created. The provider delegates the setup to Azure CLI commands, while still providing Pulumi experience and lifecycle management.
+
+In addition to the Storage itself, a CDN is configured to serve files from the Blob container origin. This may be useful if you need to serve files via HTTPS from a custom domain (not shown in the example).
+
+## Running the App
+
+1. Create a new stack:
+
+ ```
+ $ pulumi stack init dev
+ ```
+
+1. Login to Azure CLI (you will be prompted to do this during deployment if you forget this step):
+
+ ```
+ $ az login
+ ```
+
+1. Restore NPM dependencies:
+
+ ```
+ $ npm install
+ ```
+
+1. Run `pulumi up` to preview and deploy changes:
+
+ ```
+ $ pulumi up
+ Previewing changes:
+ ...
+
+ Performing changes:
+ ...
+ Resources:
+ + 8 created
+ Duration: 2m52s
+ ```
+
+1. Check the deployed website endpoint:
+
+ ```
+ $ pulumi stack output staticEndpoint
+ https://websitesbc90978a1.z20.web.core.windows.net/
+ $ curl "$(pulumi stack output staticEndpoint)"
+ <html>
+ <body>
+ <h1>This file is served from Blob Storage</h1>
+ </body>
+ </html>
+ ```

ADD
azure-ts-static-website/index.ts
+// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
+
+import * as pulumi from "@pulumi/pulumi";
+import * as azure from "@pulumi/azure";
+import { StorageStaticWebsite } from "./staticWebsite";
+
+// Create an Azure Resource Group
+const resourceGroup = new azure.core.ResourceGroup("website-rg", {
+ location: "West US",
+});
+
+// Create a Storage Account for our static website
+const storageAccount = new azure.storage.Account("websitesa", {
+ resourceGroupName: resourceGroup.name,
+ accountReplicationType: "LRS",
+ accountTier: "Standard",
+ accountKind: "StorageV2",
+});
+
+// There's currently no way to enable the Static Web Site feature of a storage account via ARM
+// Therefore, we created a custom resource which wraps corresponding Azure CLI commands
+const staticWebsite = new StorageStaticWebsite("website-static", {
+ accountName: storageAccount.name,
+});
+
+// Upload the files
+["index.html", "404.html"].map(name =>
+ new azure.storage.Blob(name, {
+ name,
+ resourceGroupName: resourceGroup.name,
+ storageAccountName: storageAccount.name,
+ storageContainerName: staticWebsite.webContainerName,
+ type: "block",
+ source: `./wwwroot/${name}`,
+ contentType: "text/html",
+ })
+);
+
+// Web endpoint to the website
+export const staticEndpoint = staticWebsite.endpoint;
+
+// Optionally, we can add a CDN in front of the website
+const cdn = new azure.cdn.Profile("website-cdn", {
+ resourceGroupName: resourceGroup.name,
+ sku: "Standard_Microsoft",
+});
+
+const endpoint = new azure.cdn.Endpoint("website-cdn-ep", {
+ resourceGroupName: resourceGroup.name,
+ profileName: cdn.name,
+ originHostHeader: staticWebsite.hostName,
+ origins: [{
+ name: "blobstorage",
+ hostName: staticWebsite.hostName,
+ }],
+});
+
+// CDN endpoint to the website.
+// Allow it some time after the deployment to get ready.
+export const cdnEndpoint = pulumi.interpolate`https://${endpoint.hostName}/`;

ADD
azure-ts-static-website/package.json
+{
+ "name": "azure-ts-static-website",
+ "devDependencies": {
+ "@types/node": "latest"
+ },
+ "dependencies": {
+ "@pulumi/pulumi": "latest",
+ "@pulumi/azure": "latest"
+ }
+}

ADD
azure-ts-static-website/staticWebsite.ts
+// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
+
+import * as pulumi from "@pulumi/pulumi";
+
+export interface StorageStaticWebsiteArgs {
+ accountName: pulumi.Input<string>;
+}
+
+const accountNameProp = "accountName";
+
+// There's currently no way to enable the Static Web Site feature of a storage account via ARM
+// Therefore, we created a custom provider which wraps corresponding Azure CLI commands
+class StorageStaticWebsiteProvider implements pulumi.dynamic.ResourceProvider {
+
+ check = async (olds: any, news: any) => {
+ const failedChecks = [];
+
+ if (news[accountNameProp] === undefined) {
+ failedChecks.push({property: accountNameProp, reason: `required property '${accountNameProp}' missing`});
+ }
+
+ return { inputs: news, failedChecks: failedChecks };
+ }
+
+ diff = async (id: pulumi.ID, olds: any, news: any) => {
+ const replaces = [];
+
+ if (olds[accountNameProp] !== news[accountNameProp]) {
+ replaces.push(accountNameProp);
+ }
+
+ return { replaces: replaces };
+ }
+
+ create = async (inputs: any) => {
+ const { execSync } = require("child_process");
+ execSync("az extension add --name storage-preview", { stdio: "ignore" });
+
+ const accountName = inputs[accountNameProp];
+ const executeToJson = (command: string) => JSON.parse(execSync(command, { stdio: ["pipe", "pipe", "ignore"] }).toString());
+ const update = executeToJson(`az storage blob service-properties update --account-name "${accountName}" --static-website --404-document 404.html --index-document index.html`);
+ if (!update.staticWebsite.enabled) {
+ throw new Error(`Static website update failed: ${update}`);
+ }
+
+ const endpoint = executeToJson(`az storage account show -n "${accountName}" --query "primaryEndpoints.web"`);
+ const hostName = endpoint.replace("https://", "").replace("/", "");
+ const webContainerName = "$web";
+ return {
+ id: `${accountName}StaticWebsite`,
+ outs: { endpoint, hostName, webContainerName }
+ };
+ }
+}
+
+export class StorageStaticWebsite extends pulumi.dynamic.Resource {
+ public readonly endpoint: pulumi.Output<string>;
+ public readonly hostName: pulumi.Output<string>;
+ public readonly webContainerName: pulumi.Output<string>;
+
+ constructor(name: string, args: StorageStaticWebsiteArgs, opts?: pulumi.CustomResourceOptions) {
+ super(new StorageStaticWebsiteProvider(), name, { ...args, endpoint: undefined, hostName: undefined, webContainerName: undefined }, opts);
+ }
+}

ADD
azure-ts-static-website/tsconfig.json
+{
+ "compilerOptions": {
+ "outDir": "bin",
+ "target": "es6",
+ "lib": [
+ "es6"
+ ],
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "experimentalDecorators": true,
+ "pretty": true,
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitAny": true,
+ "noImplicitReturns": true,
+ "forceConsistentCasingInFileNames": true,
+ "strictNullChecks": true
+ },
+ "files": [
+ "index.ts",
+ "staticWebsite.ts"
+ ]
+}

ADD
azure-ts-static-website/wwwroot/404.html
+<html>
+ <body>
+ <h1>That's a 404! Still, from the Blob Storage.</h1>
+ </body>
+</html>
\ No newline at end of file

ADD
azure-ts-static-website/wwwroot/index.html
+<html>
+ <body>
+ <h1>This file is served from Blob Storage</h1>
+ </body>
+</html>
\ No newline at end of file

Azure Static Website