diff --git a/.github/workflows/bicep.yml b/.github/workflows/bicep.yml new file mode 100644 index 0000000..5fc187d --- /dev/null +++ b/.github/workflows/bicep.yml @@ -0,0 +1,46 @@ +name: Bicep + +on: + pull_request: + push: + branches: ["main"] + +env: + CLASS_RG: rg-hyf-students + +jobs: + what-if: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + - name: Preview deployment + run: | + az deployment group what-if \ + --resource-group "$CLASS_RG" \ + --template-file main.bicep \ + --parameters \ + storageName=sthyfhalyna \ + environment=dev \ + dbAdminPassword=not-a-real-secret + + deploy: + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + - name: Apply deployment + run: | + az deployment group create \ + --resource-group "$CLASS_RG" \ + --template-file main.bicep \ + --parameters \ + storageName=sthyfhalyna \ + environment=dev \ + dbAdminPassword=not-a-real-secret diff --git a/AI_ASSIST.md b/AI_ASSIST.md index 6b56475..e6e4460 100644 --- a/AI_ASSIST.md +++ b/AI_ASSIST.md @@ -2,12 +2,25 @@ ## Prompt - +I asked an LLM to explain how to pass an `environment` parameter from `main.bicep` to a storage module, how to use that parameter as an Azure resource tag, and how to declare a second nested blob container under the same storage account. ## Model output - +The model explained that the `environment` parameter should be declared in `main.bicep`, passed through the module `params` block, and declared again in `modules/storage.bicep`. + +It also suggested using the parameter in the storage account tags and adding `curated` as a second child container under the existing default blob service. ## What I changed or verified - +I reviewed the suggested structure against the assignment instructions and the existing starter files. + +I verified that: + +- the original module structure was preserved; +- the `environment` value is passed correctly into the module; +- the storage account receives the `Environment` tag; +- both `raw` and `curated` are child resources of the default blob service; +- the existing `@secure()` parameter remains in place; +- no real passwords, credentials, keys, or connection strings are committed. + +I then ran the local assignment tests and used Azure CLI commands myself to perform the deployment and `what-if` checks. I also verified the storage account and both containers in the Azure portal. diff --git a/README.md b/README.md index 8cf33d9..0b65372 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Week 14 Assignment: Infrastructure as Code (Bicep) +**Student:** Halyna Romanyshyn + Extend a Chapter 4–complete Bicep starter (storage account + nested `raw` container) with an environment tag and a second `curated` container, then deploy, preview with `what-if`, confirm in the portal, and write it up. diff --git a/WRITEUP.md b/WRITEUP.md index c7cc144..491b937 100644 --- a/WRITEUP.md +++ b/WRITEUP.md @@ -1,4 +1,5 @@ # Write-up - +I extended the existing Bicep starter instead of rebuilding it from an empty file. The main template now has an `environment` parameter with the values `dev` and `prod`. This value is passed to the storage module and becomes the `Environment` tag on the storage account. The same Bicep files can therefore describe different environments without copying the infrastructure code. + +The storage module still creates the original nested `raw` container and now also creates a second nested container named `curated`. The `raw` container can hold data as it first arrives, while `curated` can hold data that has been cleaned or prepared for further use. Both containers use the default blob service and `parent:` so Azure understands their relationship to the storage account. diff --git a/docs/deploy_succeeded.txt b/docs/deploy_succeeded.txt index dbd008b..93ef7df 100644 --- a/docs/deploy_succeeded.txt +++ b/docs/deploy_succeeded.txt @@ -1,2 +1,120 @@ - +{ + "id": "/subscriptions/1120c89d-2a5f-4a15-a582-2ea34f0bb5c3/resourceGroups/rg-hyf-students/providers/Microsoft.Resources/deployments/week14-halyna-dev", + "location": null, + "name": "week14-halyna-dev", + "properties": { + "correlationId": "3cbb08d5-96bc-46e1-912a-5c4285c2bc05", + "debugSetting": null, + "dependencies": [], + "diagnostics": null, + "duration": "PT8.9628026S", + "error": null, + "extensions": [], + "mode": "Incremental", + "onErrorDeployment": null, + "outputResources": [ + { + "apiVersion": null, + "extension": null, + "id": "/subscriptions/1120c89d-2a5f-4a15-a582-2ea34f0bb5c3/resourceGroups/rg-hyf-students/providers/Microsoft.Storage/storageAccounts/sthyfhalyna", + "identifiers": null, + "resourceGroup": "rg-hyf-students", + "resourceType": "Microsoft.Storage/storageAccounts" + }, + { + "apiVersion": null, + "extension": null, + "id": "/subscriptions/1120c89d-2a5f-4a15-a582-2ea34f0bb5c3/resourceGroups/rg-hyf-students/providers/Microsoft.Storage/storageAccounts/sthyfhalyna/blobServices/default", + "identifiers": null, + "resourceGroup": "rg-hyf-students", + "resourceType": "Microsoft.Storage/storageAccounts/blobServices" + }, + { + "apiVersion": null, + "extension": null, + "id": "/subscriptions/1120c89d-2a5f-4a15-a582-2ea34f0bb5c3/resourceGroups/rg-hyf-students/providers/Microsoft.Storage/storageAccounts/sthyfhalyna/blobServices/default/containers/curated", + "identifiers": null, + "resourceGroup": "rg-hyf-students", + "resourceType": "Microsoft.Storage/storageAccounts/blobServices/containers" + }, + { + "apiVersion": null, + "extension": null, + "id": "/subscriptions/1120c89d-2a5f-4a15-a582-2ea34f0bb5c3/resourceGroups/rg-hyf-students/providers/Microsoft.Storage/storageAccounts/sthyfhalyna/blobServices/default/containers/raw", + "identifiers": null, + "resourceGroup": "rg-hyf-students", + "resourceType": "Microsoft.Storage/storageAccounts/blobServices/containers" + } + ], + "outputs": { + "curatedContainerName": { + "type": "String", + "value": "curated" + }, + "rawContainerName": { + "type": "String", + "value": "raw" + }, + "storageId": { + "type": "String", + "value": "/subscriptions/1120c89d-2a5f-4a15-a582-2ea34f0bb5c3/resourceGroups/rg-hyf-students/providers/Microsoft.Storage/storageAccounts/sthyfhalyna" + } + }, + "parameters": { + "containerName": { + "type": "String", + "value": "raw" + }, + "dbAdminPassword": { + "type": "SecureString" + }, + "environment": { + "type": "String", + "value": "dev" + }, + "location": { + "type": "String", + "value": "westeurope" + }, + "storageName": { + "type": "String", + "value": "sthyfhalyna" + } + }, + "parametersLink": null, + "providers": [ + { + "id": null, + "namespace": "Microsoft.Resources", + "providerAuthorizationConsentState": null, + "registrationPolicy": null, + "registrationState": null, + "resourceTypes": [ + { + "aliases": null, + "apiProfiles": null, + "apiVersions": null, + "capabilities": null, + "defaultApiVersion": null, + "locationMappings": null, + "locations": [ + null + ], + "properties": null, + "resourceType": "deployments", + "zoneMappings": null + } + ] + } + ], + "provisioningState": "Succeeded", + "templateHash": "14701220362417228026", + "templateLink": null, + "timestamp": "2026-08-03T21:21:50.625432+00:00", + "validatedResources": null, + "validationLevel": null + }, + "resourceGroup": "rg-hyf-students", + "tags": null, + "type": "Microsoft.Resources/deployments" +} diff --git a/docs/portal_confirm.md b/docs/portal_confirm.md index 4ac2c32..53780a3 100644 --- a/docs/portal_confirm.md +++ b/docs/portal_confirm.md @@ -1,8 +1,11 @@ # Portal confirmation - +I confirmed storage account `sthyfhalyna` in resource group `rg-hyf-students`. Under Data storage → Containers, both `raw` and `curated` were present. The Deployments page showed the `week14-halyna-dev` deployment with status Succeeded. - +Screenshots: + +- `screenshots/storage_account_confirm.png` +- `screenshots/containers_raw_curated_confirm.png` +- `screenshots/deployment_succeeded_confirm.png` + +Teardown: deleted `sthyfhalyna` and its nested containers on 2026-08-04. diff --git a/docs/screenshots/containers_raw_curated_confirm.png b/docs/screenshots/containers_raw_curated_confirm.png new file mode 100644 index 0000000..f17602e Binary files /dev/null and b/docs/screenshots/containers_raw_curated_confirm.png differ diff --git a/docs/screenshots/deployment_succeeded_confirm.png b/docs/screenshots/deployment_succeeded_confirm.png new file mode 100644 index 0000000..3d31db1 Binary files /dev/null and b/docs/screenshots/deployment_succeeded_confirm.png differ diff --git a/docs/screenshots/storage_account_confirm.png b/docs/screenshots/storage_account_confirm.png new file mode 100644 index 0000000..07857fc Binary files /dev/null and b/docs/screenshots/storage_account_confirm.png differ diff --git a/docs/what_if.txt b/docs/what_if.txt index 328fcda..93e60f3 100644 --- a/docs/what_if.txt +++ b/docs/what_if.txt @@ -1 +1,40 @@ - +Note: The result may contain false positive predictions (noise). +You can help us improve the accuracy of the result by opening an issue here: https://aka.ms/WhatIfIssues + +Resource and property changes are indicated with these symbols: + - Delete + ~ Modify + * Ignore + +The deployment will update the following scope: + +Scope: /subscriptions/1120c89d-2a5f-4a15-a582-2ea34f0bb5c3/resourceGroups/rg-hyf-students + + ~ Microsoft.Storage/storageAccounts/sthyfhalyna [2023-01-01] + ~ tags.environment: "dev" => "prod" + + ~ Microsoft.Storage/storageAccounts/sthyfhalyna/blobServices/default [2023-01-01] + - properties: + + deleteRetentionPolicy.allowPermanentDelete: false + deleteRetentionPolicy.enabled: false + + + ~ Microsoft.Storage/storageAccounts/sthyfhalyna/blobServices/default/containers/curated [2023-01-01] + - properties.defaultEncryptionScope: "$account-encryption-key" + - properties.denyEncryptionScopeOverride: false + + ~ Microsoft.Storage/storageAccounts/sthyfhalyna/blobServices/default/containers/raw [2023-01-01] + - properties.defaultEncryptionScope: "$account-encryption-key" + - properties.denyEncryptionScopeOverride: false + + * Microsoft.Storage/storageAccounts/sthyfbader + * Microsoft.Storage/storageAccounts/sthyfbaraah + * Microsoft.Storage/storageAccounts/sthyfhannah + * Microsoft.Storage/storageAccounts/sthyfimper0802 + * Microsoft.Storage/storageAccounts/sthyflasseb + * Microsoft.Storage/storageAccounts/sthyfmohammedalfakih + * Microsoft.Storage/storageAccounts/sthyfpavel + * Microsoft.Storage/storageAccounts/sthyfw14lab + +Resource changes: 4 to modify, 8 to ignore. diff --git a/main.bicep b/main.bicep index 50b8790..2d5a1d9 100644 --- a/main.bicep +++ b/main.bicep @@ -12,6 +12,12 @@ param location string = resourceGroup().location param storageName string param containerName string = 'raw' +@allowed([ + 'dev' + 'prod' +]) +param environment string = 'dev' + // Dummy unused secret for hygiene practice — pass at deploy time, never commit the value @secure() param dbAdminPassword string @@ -22,7 +28,10 @@ module storage 'modules/storage.bicep' = { location: location storageName: storageName containerName: containerName + environment: environment } } output storageId string = storage.outputs.storageId +output rawContainerName string = storage.outputs.rawContainerName +output curatedContainerName string = storage.outputs.curatedContainerName diff --git a/modules/storage.bicep b/modules/storage.bicep index 34ac8e8..87ec47d 100644 --- a/modules/storage.bicep +++ b/modules/storage.bicep @@ -5,11 +5,20 @@ param location string param storageName string param containerName string = 'raw' +param environment string resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = { name: storageName location: location - sku: { name: 'Standard_LRS' } + + tags: { + environment: environment + } + + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' } @@ -18,7 +27,7 @@ resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' name: 'default' } -resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = { +resource rawcontainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = { parent: blobService name: containerName properties: { @@ -26,4 +35,14 @@ resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@20 } } +resource curatedcontainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = { + parent: blobService + name: 'curated' + properties: { + publicAccess: 'None' + } +} + output storageId string = storage.id +output rawContainerName string = rawcontainer.name +output curatedContainerName string = curatedcontainer.name