@staticforge/azure 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -2
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/clients.d.ts +23 -0
  5. package/dist/clients.d.ts.map +1 -0
  6. package/dist/clients.js +85 -0
  7. package/dist/clients.js.map +1 -0
  8. package/dist/deployer.d.ts +20 -0
  9. package/dist/deployer.d.ts.map +1 -0
  10. package/dist/deployer.js +343 -0
  11. package/dist/deployer.js.map +1 -0
  12. package/dist/history.d.ts +37 -0
  13. package/dist/history.d.ts.map +1 -0
  14. package/dist/history.js +49 -0
  15. package/dist/history.js.map +1 -0
  16. package/dist/iac.d.ts +27 -0
  17. package/dist/iac.d.ts.map +1 -0
  18. package/dist/iac.js +188 -0
  19. package/dist/iac.js.map +1 -0
  20. package/dist/index.d.ts +12 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +20 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/rebuild.d.ts +15 -0
  25. package/dist/rebuild.d.ts.map +1 -0
  26. package/dist/rebuild.js +97 -0
  27. package/dist/rebuild.js.map +1 -0
  28. package/dist/swa-config.d.ts +19 -0
  29. package/dist/swa-config.d.ts.map +1 -0
  30. package/dist/swa-config.js +136 -0
  31. package/dist/swa-config.js.map +1 -0
  32. package/dist/uploader.d.ts +24 -0
  33. package/dist/uploader.d.ts.map +1 -0
  34. package/dist/uploader.js +58 -0
  35. package/dist/uploader.js.map +1 -0
  36. package/package.json +52 -3
  37. package/terraform/modules/site/main.tf +38 -0
  38. package/terraform/modules/site/outputs.tf +27 -0
  39. package/terraform/modules/site/variables.tf +44 -0
  40. package/terraform/modules/site/versions.tf +10 -0
  41. package/terraform/state-bootstrap/main.tf +83 -0
  42. package/terraform/state-bootstrap/outputs.tf +21 -0
  43. package/terraform/state-bootstrap/variables.tf +53 -0
  44. package/terraform/state-bootstrap/versions.tf +14 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploader.js","sourceRoot":"","sources":["../src/uploader.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,55 @@
1
1
  {
2
2
  "name": "@staticforge/azure",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Azure provider for staticforge: Static Web Apps, named preview environments, rollback by rebuild",
5
+ "keywords": [
6
+ "staticforge",
7
+ "azure",
8
+ "static-web-apps",
9
+ "opentofu",
10
+ "terraform"
11
+ ],
12
+ "homepage": "https://github.com/elchanio-76/sforge#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/elchanio-76/sforge/issues"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/elchanio-76/sforge.git",
19
+ "directory": "packages/providers/azure"
20
+ },
21
+ "license": "MIT",
22
+ "author": "Eleftherios Chaniotakis",
23
+ "type": "module",
24
+ "exports": {
25
+ ".": {
26
+ "@staticforge/source": "./src/index.ts",
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.js"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "terraform/**/*.tf",
34
+ "terraform/**/*.hcl",
35
+ "!terraform/**/.terraform.lock.hcl",
36
+ "!terraform/**/tests/**"
37
+ ],
38
+ "engines": {
39
+ "node": "^22.22.0 || >=24.8.0"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "dependencies": {
45
+ "@azure/arm-appservice": "^19.0.0",
46
+ "@azure/arm-storage": "^21.0.0",
47
+ "@azure/identity": "^4.14.0",
48
+ "@azure/static-web-apps-cli": "^2.1.0",
49
+ "@azure/storage-blob": "^12.34.0",
50
+ "@staticforge/build": "0.1.0",
51
+ "@staticforge/core": "0.1.0",
52
+ "@staticforge/iac": "0.1.0",
53
+ "@staticforge/provider-api": "0.1.0"
54
+ }
6
55
  }
@@ -0,0 +1,38 @@
1
+ # An Azure Static Web App that sforge deploys to with the SWA deployment client.
2
+ # The app is not linked to a repository: content arrives through `sforge deploy`,
3
+ # which fetches the deployment token at run time (it is not an output and not in
4
+ # any file sforge writes).
5
+
6
+ locals {
7
+ # Static Web App names are 2-60 characters.
8
+ app_name = substr("${var.name}-${var.target}", 0, 60)
9
+ tags = merge(var.tags, { "sforge:role" = "site" })
10
+ }
11
+
12
+ resource "azurerm_resource_group" "site" {
13
+ count = var.create_resource_group ? 1 : 0
14
+
15
+ name = var.resource_group_name
16
+ location = var.location
17
+ tags = local.tags
18
+ }
19
+
20
+ data "azurerm_resource_group" "site" {
21
+ count = var.create_resource_group ? 0 : 1
22
+
23
+ name = var.resource_group_name
24
+ }
25
+
26
+ resource "azurerm_static_web_app" "site" {
27
+ name = local.app_name
28
+ resource_group_name = var.create_resource_group ? azurerm_resource_group.site[0].name : data.azurerm_resource_group.site[0].name
29
+ location = var.location
30
+
31
+ sku_tier = var.sku
32
+ sku_size = var.sku
33
+
34
+ # Named preview environments (`sforge deploy --preview pr-42`).
35
+ preview_environments_enabled = true
36
+
37
+ tags = local.tags
38
+ }
@@ -0,0 +1,27 @@
1
+ output "static_web_app_name" {
2
+ value = azurerm_static_web_app.site.name
3
+ }
4
+
5
+ output "static_web_app_id" {
6
+ value = azurerm_static_web_app.site.id
7
+ }
8
+
9
+ output "resource_group" {
10
+ value = var.resource_group_name
11
+ }
12
+
13
+ output "location" {
14
+ value = var.location
15
+ }
16
+
17
+ output "sku" {
18
+ value = var.sku
19
+ }
20
+
21
+ output "default_host_name" {
22
+ value = azurerm_static_web_app.site.default_host_name
23
+ }
24
+
25
+ output "url" {
26
+ value = "https://${azurerm_static_web_app.site.default_host_name}"
27
+ }
@@ -0,0 +1,44 @@
1
+ variable "name" {
2
+ description = "Project name (iac.name), used in resource names and tags."
3
+ type = string
4
+ }
5
+
6
+ variable "target" {
7
+ description = "Target name, e.g. prod."
8
+ type = string
9
+ }
10
+
11
+ variable "resource_group_name" {
12
+ description = "Resource group of the site."
13
+ type = string
14
+ }
15
+
16
+ variable "create_resource_group" {
17
+ description = "Create the resource group (and delete it with the site). False uses an existing one."
18
+ type = bool
19
+ default = true
20
+ }
21
+
22
+ variable "location" {
23
+ description = "Azure region. Static Web Apps are only available in a few (westus2, centralus, eastus2, westeurope, eastasia)."
24
+ type = string
25
+ validation {
26
+ condition = contains(["westus2", "centralus", "eastus2", "westeurope", "eastasia"], var.location)
27
+ error_message = "Static Web Apps are available in westus2, centralus, eastus2, westeurope and eastasia."
28
+ }
29
+ }
30
+
31
+ variable "sku" {
32
+ description = "Free or Standard."
33
+ type = string
34
+ default = "Free"
35
+ validation {
36
+ condition = contains(["Free", "Standard"], var.sku)
37
+ error_message = "sku must be \"Free\" or \"Standard\"."
38
+ }
39
+ }
40
+
41
+ variable "tags" {
42
+ type = map(string)
43
+ default = {}
44
+ }
@@ -0,0 +1,10 @@
1
+ terraform {
2
+ required_version = ">= 1.10.0"
3
+
4
+ required_providers {
5
+ azurerm = {
6
+ source = "hashicorp/azurerm"
7
+ version = ">= 4.0, < 5.0"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,83 @@
1
+ # Remote state for every staticforge site in one Azure subscription.
2
+ # State files are stored in a private blob container at <site>/<target>.tfstate,
3
+ # authenticated with Microsoft Entra ID (no access keys) and locked with blob leases.
4
+
5
+ data "azurerm_client_config" "current" {}
6
+
7
+ resource "azurerm_resource_group" "state" {
8
+ name = var.resource_group_name
9
+ location = var.location
10
+ tags = merge(var.tags, { "sforge:role" = "state" })
11
+
12
+ lifecycle {
13
+ prevent_destroy = true
14
+ }
15
+ }
16
+
17
+ resource "azurerm_storage_account" "state" {
18
+ name = var.storage_account_name
19
+ resource_group_name = azurerm_resource_group.state.name
20
+ location = azurerm_resource_group.state.location
21
+
22
+ account_kind = "StorageV2"
23
+ account_tier = "Standard"
24
+ account_replication_type = var.replication_type
25
+
26
+ min_tls_version = "TLS1_2"
27
+ https_traffic_only_enabled = true
28
+ allow_nested_items_to_be_public = false
29
+ shared_access_key_enabled = false
30
+ default_to_oauth_authentication = true
31
+
32
+ blob_properties {
33
+ versioning_enabled = true
34
+
35
+ delete_retention_policy {
36
+ days = var.retention_days
37
+ }
38
+
39
+ container_delete_retention_policy {
40
+ days = var.retention_days
41
+ }
42
+ }
43
+
44
+ tags = merge(var.tags, { "sforge:role" = "state" })
45
+
46
+ lifecycle {
47
+ prevent_destroy = true
48
+ }
49
+ }
50
+
51
+ resource "azurerm_storage_container" "state" {
52
+ name = var.container_name
53
+ storage_account_id = azurerm_storage_account.state.id
54
+ container_access_type = "private"
55
+
56
+ lifecycle {
57
+ prevent_destroy = true
58
+ }
59
+ }
60
+
61
+ # Owner and Contributor manage the account but cannot read blobs: data access is a separate role.
62
+ resource "azurerm_role_assignment" "caller" {
63
+ scope = azurerm_storage_account.state.id
64
+ role_definition_name = "Storage Blob Data Contributor"
65
+ principal_id = data.azurerm_client_config.current.object_id
66
+ }
67
+
68
+ resource "azurerm_role_assignment" "extra" {
69
+ for_each = toset(var.data_contributors)
70
+
71
+ scope = azurerm_storage_account.state.id
72
+ role_definition_name = "Storage Blob Data Contributor"
73
+ principal_id = each.value
74
+ }
75
+
76
+ resource "time_sleep" "role_propagation" {
77
+ create_duration = var.role_propagation_delay
78
+
79
+ depends_on = [
80
+ azurerm_role_assignment.caller,
81
+ azurerm_role_assignment.extra,
82
+ ]
83
+ }
@@ -0,0 +1,21 @@
1
+ # Both depend on the role propagation, so the migration into the backend starts after it.
2
+
3
+ output "resource_group" {
4
+ value = azurerm_resource_group.state.name
5
+ depends_on = [time_sleep.role_propagation]
6
+ }
7
+
8
+ output "storage_account" {
9
+ value = azurerm_storage_account.state.name
10
+ depends_on = [time_sleep.role_propagation]
11
+ }
12
+
13
+ output "storage_account_id" {
14
+ value = azurerm_storage_account.state.id
15
+ depends_on = [time_sleep.role_propagation]
16
+ }
17
+
18
+ output "container" {
19
+ value = azurerm_storage_container.state.name
20
+ depends_on = [time_sleep.role_propagation]
21
+ }
@@ -0,0 +1,53 @@
1
+ variable "resource_group_name" {
2
+ description = "Resource group of the state storage account, e.g. sforge-state."
3
+ type = string
4
+ }
5
+
6
+ variable "location" {
7
+ description = "Azure region of the state storage account."
8
+ type = string
9
+ }
10
+
11
+ variable "storage_account_name" {
12
+ description = "Globally unique name (3-24 lowercase letters and digits), e.g. sfstate0123456789ab."
13
+ type = string
14
+ validation {
15
+ condition = can(regex("^[a-z0-9]{3,24}$", var.storage_account_name))
16
+ error_message = "storage_account_name must be 3-24 lowercase letters and digits."
17
+ }
18
+ }
19
+
20
+ variable "container_name" {
21
+ description = "Blob container that holds the state files."
22
+ type = string
23
+ default = "tfstate"
24
+ }
25
+
26
+ variable "replication_type" {
27
+ description = "Storage replication (LRS, ZRS, GRS, ...)."
28
+ type = string
29
+ default = "LRS"
30
+ }
31
+
32
+ variable "retention_days" {
33
+ description = "Days that deleted blobs and containers (and so state files) stay recoverable."
34
+ type = number
35
+ default = 30
36
+ }
37
+
38
+ variable "data_contributors" {
39
+ description = "Object IDs of further principals that may read and write state. The caller always can."
40
+ type = list(string)
41
+ default = []
42
+ }
43
+
44
+ variable "role_propagation_delay" {
45
+ description = "Wait after granting data access: Azure RBAC takes minutes to take effect, and the state is migrated right after."
46
+ type = string
47
+ default = "120s"
48
+ }
49
+
50
+ variable "tags" {
51
+ type = map(string)
52
+ default = {}
53
+ }
@@ -0,0 +1,14 @@
1
+ terraform {
2
+ required_version = ">= 1.10.0"
3
+
4
+ required_providers {
5
+ azurerm = {
6
+ source = "hashicorp/azurerm"
7
+ version = ">= 4.0, < 5.0"
8
+ }
9
+ time = {
10
+ source = "hashicorp/time"
11
+ version = ">= 0.12, < 1.0"
12
+ }
13
+ }
14
+ }