Quickstart: Bereitstellung statischer Webseite auf Azure

In diesem Artikel möchte ich die Schritte für das Veröffentlichen einer statischen Webseite, zum Beispiel einer "Landing Page" mit Terraform und Azure zeigen.

Voraussetzung

  • Ein Azure Konto ist eingerichtet.
  • Die Azure Cli Tools müssen für das jeweilige Zielsystem installiert sein.
  • Terraform ist installiert und konfiguriert für den Zugriff auf Azure.

Vorgehen

Folgende Schritte werden in der Terraform ausgeführt, damit eine statische Webseite auf Azure veröffentlicht werden kann.

StorageAccount und statische WebApp erstellen

Im ersten Schritt wird ein StorageAccount erstellt.

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=3.0.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}

  subscription_id = "YOUR SUBSCRIPTION"
  client_id       = "YOU APPLICATION ID"
  client_secret   = "YOUR APPLICATION SECRET"
  tenant_id       = "YOUR TENANT ID"
}

resource "azurerm_resource_group" "rg" {
  name = "terrfaform-playground"
  # Westeurope da statische Webseiten in der Schweiz
  # noch nicht verfügbar sind.
  location = "westeurope"
}

resource "azurerm_storage_account" "storage" {
  account_tier = "Standard"
  account_kind = "StorageV2"
  account_replication_type = "LRS"
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  name = "schaedldstorage"  
  allow_nested_items_to_be_public = true
  static_website {
    index_document = "index.html"
  }
}

Die Befehle terraform init, terraform plan -out sampleplan, terraform apply sampleplan und terraform destroy (In Produktion eher vorsichtig damit umgehen) ausgeführt. Diese sind durchgängig durch das ganze Beispiel immer wieder anzuwenden.

Terraform init


terraform init

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/azurerm versions matching "3.0.0"...
- Installing hashicorp/azurerm v3.0.0...
- Installed hashicorp/azurerm v3.0.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Terraform plan

     terraform plan -out simpleplan

Terraform used the selected providers to generate the following execution plan. Resource actions are    
indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_resource_group.rg will be created
  + resource "azurerm_resource_group" "rg" {
      + id       = (known after apply)
      + location = "westeurope"
      + name     = "terrfaform-playground"
    }

  # azurerm_static_site.website will be created
  + resource "azurerm_static_site" "website" {
      + api_key             = (known after apply)
      + default_host_name   = (known after apply)
      + id                  = (known after apply)
      + location            = "westeurope"
      + name                = "sample-web-app"
      + resource_group_name = "terrfaform-playground"
      + sku_size            = "Free"
      + sku_tier            = "Free"
    }

  # azurerm_storage_account.storage will be created
  + resource "azurerm_storage_account" "storage" {
      + access_tier                       = (known after apply)
      + account_kind                      = "StorageV2"
      + account_replication_type          = "LRS"
      + account_tier                      = "Standard"
      + allow_nested_items_to_be_public   = true
      + enable_https_traffic_only         = true
      + id                                = (known after apply)
      + infrastructure_encryption_enabled = false
      + is_hns_enabled                    = false
      + large_file_share_enabled          = (known after apply)
      + location                          = "westeurope"
      + min_tls_version                   = "TLS1_2"
      + name                              = "schaedldstorage"
      + nfsv3_enabled                     = false
      + primary_access_key                = (sensitive value)
      + primary_blob_connection_string    = (sensitive value)
      + primary_blob_endpoint             = (known after apply)
      + primary_blob_host                 = (known after apply)
      + primary_connection_string         = (sensitive value)
      + primary_dfs_endpoint              = (known after apply)
      + primary_dfs_host                  = (known after apply)
      + primary_file_endpoint             = (known after apply)
      + primary_file_host                 = (known after apply)
      + primary_location                  = (known after apply)
      + primary_queue_endpoint            = (known after apply)
      + primary_queue_host                = (known after apply)
      + primary_table_endpoint            = (known after apply)
      + primary_table_host                = (known after apply)
      + primary_web_endpoint              = (known after apply)
      + primary_web_host                  = (known after apply)
      + queue_encryption_key_type         = "Service"
      + resource_group_name               = "terrfaform-playground"
      + secondary_access_key              = (sensitive value)
      + secondary_blob_connection_string  = (sensitive value)
      + secondary_blob_endpoint           = (known after apply)
      + secondary_blob_host               = (known after apply)
      + secondary_connection_string       = (sensitive value)
      + secondary_dfs_endpoint            = (known after apply)
      + secondary_dfs_host                = (known after apply)
      + secondary_file_endpoint           = (known after apply)
      + secondary_file_host               = (known after apply)
      + secondary_location                = (known after apply)
      + secondary_queue_endpoint          = (known after apply)
      + secondary_queue_host              = (known after apply)
      + secondary_table_endpoint          = (known after apply)
      + secondary_table_host              = (known after apply)
      + secondary_web_endpoint            = (known after apply)
      + secondary_web_host                = (known after apply)
      + shared_access_key_enabled         = true
      + table_encryption_key_type         = "Service"

      + blob_properties {
          + change_feed_enabled      = (known after apply)
          + default_service_version  = (known after apply)
          + last_access_time_enabled = (known after apply)
          + versioning_enabled       = (known after apply)

          + container_delete_retention_policy {
              + days = (known after apply)
            }

          + cors_rule {
              + allowed_headers    = (known after apply)
              + allowed_methods    = (known after apply)
              + allowed_origins    = (known after apply)
              + exposed_headers    = (known after apply)
              + max_age_in_seconds = (known after apply)
            }

          + delete_retention_policy {
              + days = (known after apply)
            }
        }

      + network_rules {
          + bypass                     = (known after apply)
          + default_action             = (known after apply)
          + ip_rules                   = (known after apply)
          + virtual_network_subnet_ids = (known after apply)

          + private_link_access {
              + endpoint_resource_id = (known after apply)
              + endpoint_tenant_id   = (known after apply)
            }
        }

      + queue_properties {
          + cors_rule {
              + allowed_headers    = (known after apply)
              + allowed_methods    = (known after apply)
              + allowed_origins    = (known after apply)
              + exposed_headers    = (known after apply)
              + max_age_in_seconds = (known after apply)
            }

          + hour_metrics {
              + enabled               = (known after apply)
              + include_apis          = (known after apply)
              + retention_policy_days = (known after apply)
              + version               = (known after apply)
            }

          + logging {
              + delete                = (known after apply)
              + read                  = (known after apply)
              + retention_policy_days = (known after apply)
              + version               = (known after apply)
              + write                 = (known after apply)
            }

          + minute_metrics {
              + enabled               = (known after apply)
              + include_apis          = (known after apply)
              + retention_policy_days = (known after apply)
              + version               = (known after apply)
            }
        }

      + routing {
          + choice                      = (known after apply)
          + publish_internet_endpoints  = (known after apply)
          + publish_microsoft_endpoints = (known after apply)
        }

      + share_properties {
          + cors_rule {
              + allowed_headers    = (known after apply)
              + allowed_methods    = (known after apply)
              + allowed_origins    = (known after apply)
              + exposed_headers    = (known after apply)
              + max_age_in_seconds = (known after apply)
            }

          + retention_policy {
              + days = (known after apply)
            }

          + smb {
              + authentication_types            = (known after apply)
              + channel_encryption_type         = (known after apply)
              + kerberos_ticket_encryption_type = (known after apply)
              + versions                        = (known after apply)
            }
        }
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Terraform apply


terraform apply sampleplan    
azurerm_resource_group.rg: Creating...
azurerm_resource_group.rg: Creation complete after 0s [id=/subscriptions/YOUR SUBSCRIPTION/resourceGroups/terrfaform-playground]
azurerm_storage_account.storage: Creating...
azurerm_storage_account.storage: Still creating... [11s elapsed]
azurerm_storage_account.storage: Still creating... [21s elapsed]
azurerm_storage_account.storage: Creation complete after 22s [id=/subscriptions/YOUR SUBSCRIPTION/resourceGroups/terrfaform-playground/providers/Microsoft.Storage/storageAccounts/schaedldstorage]
azurerm_static_site.website: Creating...
azurerm_static_site.website: Creation complete after 3s [id=/subscriptions/YOUR SUBSCRIPTION/resourceGroups/terrfaform-playground/providers/Microsoft.Web/staticSites/sample-web-app]        

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

In Azure ist der StorageAccount erstellt worden.

Azure: StorageAccount erstellt.

Nun können die weiteren Elemente hinzugefügt werden. Gemäss der Anleitung für das Hosten von statischen Webseiten wird noch überprüft ob die Konfiguration von Terraform mit der dokumentierten übereinstimmt.

Azure: Statische Webseite aktiviert.

Hochladen der Landing Page

Azure bietet nicht die Möglichkeit ein Objekt direkt mit Terraform im StorageAccount zu erstellen, sodass ein anderer Weg zur Publizierung gewählt werden muss. Hierzu kann einer der drei dokumentierten Wege gewählt werden:

  • Über das Portal (wenig Automatisierungspotential)
  • Über die Azure Cli Tools
  • Über die Powershell

Das Powershell script ist schnell erläutert:

$storageAccount = Get-AzStorageAccount -Name "schaedldstorage" -ResourceGroupName "terrfaform-playground"
$storageAccountContext = $storageAccount.Context
Set-AzStorageBlobContent -Context $storageAccountContext -Blob "index.html" -File "..\content\index.html" -Container `$web -Properties @{ ContentType="text/html; charset=utf-8;"}

Beim Erstellen des StorageAccounts, wird ein Container automatisch mit dem Namen $web angelegt. Diesen kann man dann für das Hosten verwenden (das Script kopiert die Datei in diesen Container.)

Azure: Web Container

Fazit

Mir nur wenig Aufwand, kann ein erster Kontaktpunkt zu einer neuen Firma auf Azure bereitgestellt werden. Dies ist nur ein Beispiel und hat noch keine Sicherheitsfunktionen aktiviert (vgl. Hosting a static Webseite in Azure Storage). Jedoch ist es weniger simple das Ganze, wie in AWS nur mit Terraform zu bewerkstelligen.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s