Lab Setup

Follow these steps to deploy the resources used in the lab:

  1. Open the Azure portal at https://portal.azure.com and sign in with User1.

  2. In the portal search bar, find and open Deploy a custom template.

  3. Select Build your own template in the editor, and then select Load file.

  4. Select lab-2a-setup.json from the F:\AllFiles\Lab-2A folder on the lab VM, and then select Save.

  5. Select Review + create, and then select Create.

  6. Wait until the deployment shows Succeeded before continuing.

===

Secure Azure Storage

A storage account containing AI training data and model outputs is currently accessible from the public internet with no network restrictions. A recent security scan found that the account has no threat detection enabled and no policies governing how access tokens are issued or revoked. Any actor with the account key or a valid SAS token generated without policy backing can access training data with no audit trail and no revocation path.

Your task is to close these gaps. You will apply a stored access policy to the training data container so that any generated SAS tokens can be revoked centrally, verify that the SAS token works before any network restrictions are in place, restrict network access to an authorized virtual network only, confirm that the restriction blocks unauthorized access from Cloud Shell, and enable Defender for Storage to detect future threats.

In this lab, you will:

  • Create a stored access policy on a blob container and generate a SAS token backed by the policy
  • Verify data-plane access using the SAS token before and after network restrictions are applied
  • Restrict storage account network access to an authorized virtual network
  • Confirm the firewall blocks unauthorized access from Cloud Shell and the Azure portal
  • Enable Defender for Storage at the resource level
  • Confirm diagnostic logging to a Log Analytics workspace

This exercise should take approximately 60 minutes to complete.


Create a stored access policy and generate a SAS token

A stored access policy centralizes the permissions and lifetime of one or more SAS tokens on a container. Without a stored access policy, a SAS token cannot be revoked before its expiry — the only remediation for a leaked standalone SAS token is to rotate the storage account key, which invalidates all other tokens simultaneously. A stored access policy solves this: revoking the policy immediately invalidates every SAS token that references it, with no collateral impact on other access paths.

You will create a stored access policy on the training-data container that grants read access for 24 hours, then generate a SAS token that references the policy.

Note: The pre-provisioned storage account name begins with sc500lab2a followed by an 8-character suffix unique to your lab subscription. Throughout this lab, <storage-account-name> refers to that account. The sc500-lab2a-rg resource group contains exactly one storage account — select it whenever the lab asks for <storage-account-name>.

  1. Sign in to the Azure portal at https://portal.azure.com using your User1 credentials.

  2. In the search bar, search for and select Storage accounts.

  3. Select the storage account whose name starts with sc500lab2a (there is only one in sc500-lab2a-rg).

  4. In the left menu, under Data storage, select Containers.

  5. Select the training-data container.

  6. Select Upload.

  7. On the Upload blob pane, select Browse for files, and then select sample-1.json from the F:\AllFiles\Lab-2A folder on the lab VM.

  8. Select Upload.

  9. Confirm that sample-1.json appears in the container.

  10. In the menu on the left, select Settings then Access policy.

    Note: This opens the Access policy pane for the container — not the storage account. Stored access policies are defined at the container level.

  11. Under Stored access policies, select + Add policy.

  12. Configure the policy as follows:

    Setting Value
    Identifier sc500-training-read
    Permissions Select Read only
    Start time Leave at the current date and time
    Expiry time Set to 24 hours from now
  13. Select OK, then select Save to save the policy.

  14. Return to the training-data container overview.

  15. In the menu on the left, select Shared access tokens.

  16. On the Generate SAS panel, configure the following:

    Setting Value
    Signing method Account key
    Signing key Key 1 (default — either key works)
    Stored access policy sc500-training-read
    Permissions Automatically populated from the policy
    Start and expiry date/time Automatically populated from the policy
    Allowed IP addresses Leave blank
    Allowed protocols HTTPS only
  17. Select Generate SAS token and URL.

  18. Copy the SAS token value (not the SAS URL) and save it to a text file — you will need it in the next section. The SAS token is the query string only, beginning with sv= or si=. It does not include the https:// prefix or the container path.

    Note: This SAS token is backed by the sc500-training-read stored access policy. If the policy is deleted or its permissions are revoked, this token becomes immediately invalid — without needing to rotate the storage account key.


Verify access before applying network restrictions

Before restricting the storage account to a virtual network, verify that the SAS token provides data-plane access from Cloud Shell. This establishes the "before" state — open access from any network — so you can observe the contrast after the firewall rule is applied.

  1. In the Azure portal top bar, select the Cloud Shell icon (>_). If prompted, select Bash.

  2. Assign your SAS token to a variable, replacing the placeholder with the SAS token you copied in the previous section:

    SAS_TOKEN="<paste your SAS token here>"
    
  3. Capture the storage account name into a variable. The sc500-lab2a-rg resource group contains exactly one storage account, so this command reads the deployed name directly:

    STORAGE_NAME=$(az storage account list --resource-group sc500-lab2a-rg --query "[0].name" -o tsv)
    echo "Storage account: $STORAGE_NAME"
    

    The output shows the actual account name (for example, sc500lab2aabc12345). You will reuse $STORAGE_NAME in every curl command in this lab.

  4. Verify the SAS variable is set by checking the first few characters:

    echo "${SAS_TOKEN:0:30}..."
    

    The output should show the beginning of your SAS token (for example, si=sc500-training-read&spr=...). If the output is ... with nothing before it, the variable is empty — paste the SAS token value again and re-run the assignment.

    Warning: If the output is ... with nothing before it, the variable is empty — paste the SAS token value again and re-run the assignment. Do not proceed with an empty variable.

  5. Run the following command to read a blob from the container using the SAS token. This calls the Azure Blob Storage REST API directly — no Azure CLI authentication is involved:

    curl -s -w "\n--- HTTP Status: %{http_code} ---" \
      "https://${STORAGE_NAME}.blob.core.windows.net/training-data/sample-1.json?${SAS_TOKEN}"
    

    Confirm that the response ends with --- HTTP Status: 200 --- and the body contains the JSON content of the file. This confirms the SAS token is valid and the storage account currently allows read access from all networks.

    Note: The stored access policy grants Read permission — this allows reading individual blob content but not listing the container. Cloud Shell runs outside your virtual network, so a 200 response here confirms the account is open to all networks. After the firewall rule is applied, this same request will return 403.


Restrict network access to the authorized virtual network

You will now change the storage account from Allow all networks to Selected networks, adding the pre-provisioned sc500-lab2a-vnet subnet as the only authorized network. You will then disable the Allow Azure services exception, which otherwise creates a bypass for any Azure-hosted service regardless of network membership.

  1. In the left menu for the storage account (<storage-account-name>), under Security + networking, select Networking.

  2. Select the Public Access tab, then select Manage.

    Note: The Manage button opens the Public Network Access configuration page. If you don't see a Manage button, you may already be on the Public Network Access page.

  3. Under Public Network Access, confirm the setting is Enable.

  4. Under Public Network Access Scope, select Enable from selected networks.

  5. Under Virtual networks, select + Add existing virtual network.

  6. Configure the following:

    Setting Value
    Subscription Your lab subscription
    Virtual network sc500-lab2a-vnet
    Subnet default
  7. Select Add.

  8. Under Exceptions, clear the checkbox for Allow trusted Microsoft services to access this resource.

    Note: Leave the Allow read access to storage logging from any network and Allow read access to storage metrics from any network checkboxes enabled — these exceptions allow Azure Monitor and diagnostic logging to function even with the VNet firewall active, and align with the Logging and Metrics bypass settings in the storage account's network configuration.

    Note: The Allow trusted Microsoft services exception grants implicit data-plane access to a broad set of Microsoft services — including Azure Backup, Azure Site Recovery, and Azure Monitor — regardless of network membership. Disabling it closes a potential bypass that could allow exfiltration via a misconfigured Azure service in the same tenant. In production environments, evaluate whether any Azure services legitimately need this bypass before removing it.

  9. Select Save to apply the network restriction.


Confirm the firewall blocks unauthorized access

With the VNet-only restriction applied, verify that Cloud Shell — which runs outside the authorized VNet — is now blocked from accessing the storage account.

  1. Return to the Cloud Shell session (or reopen it if it closed). Check whether the $SAS_TOKEN and $STORAGE_NAME variables are still set:

    echo "${SAS_TOKEN:0:30}..."
    echo "Storage account: $STORAGE_NAME"
    

    If either output is empty or ..., the session timed out and the variables were cleared. Reassign them:

    SAS_TOKEN="<paste your SAS token here>"
    STORAGE_NAME=$(az storage account list --resource-group sc500-lab2a-rg --query "[0].name" -o tsv)
    
  2. Run the same blob read request:

    curl -s -w "\n--- HTTP Status: %{http_code} ---" \
      "https://${STORAGE_NAME}.blob.core.windows.net/training-data/sample-1.json?${SAS_TOKEN}"
    

    Confirm that the response body contains <Code>AuthorizationFailure</Code> and the response ends with --- HTTP Status: 403 ---. AuthorizationFailure is the error code Azure Storage returns when a request is blocked by the network firewall — the SAS token is valid, but the request origin (Cloud Shell's IP) is not in the authorized network list. This confirms the firewall is active.

  3. Close the Cloud Shell.

  4. In the left menu for the storage account (<storage-account-name>), select Storage browser.

  5. Select Blob containers, then select training-data.

    Confirm that an error appears stating the request is not authorized. The Storage browser calls the storage data plane directly — because your browser session originates from an IP address outside the authorized VNet, access is blocked here as well.

    Note: Resources deployed within the sc500-lab2a-vnet subnet — such as a virtual machine running in that network — can still access the storage account. The restriction blocks the public internet and all other networks not in the authorized VNet. This is the intended security posture for a storage account that serves only VNet-resident workloads.


Enable Defender for Storage

Defender for Storage provides threat detection for your storage account — detecting anomalous access patterns, malware uploads, and data exfiltration attempts. You will enable it at the resource level on <storage-account-name>.

Defender for Storage's malware scanning feature uses Azure Event Grid to route scan results. The Event Grid resource provider must be registered in your subscription before enabling Defender for Storage, or the enablement will partially fail.

  1. In the Azure portal top bar, select the Cloud Shell icon (>_). If prompted, select Bash.

  2. Register the Event Grid resource provider:

    az provider register --namespace Microsoft.EventGrid --wait
    
  3. Verify the registration is complete before proceeding:

    az provider show --namespace Microsoft.EventGrid --query "registrationState" -o tsv
    

    Confirm the output shows Registered before proceeding. The --wait option keeps the registration command running until Azure reports a terminal registration state.

  4. Close the Cloud Shell.

  5. In the left menu for the storage account (<storage-account-name>), under Security + networking, select Microsoft Defender for Cloud.

  6. Select Enable on storage account.

    Note: Defender for Storage can also be enabled at the subscription level from Microsoft Defender for CloudEnvironment settings → your subscription → Storage. Subscription-level enablement automatically protects all current and future storage accounts in the subscription without requiring per-resource configuration — the recommended approach for production environments. Resource-level enablement, used here, limits protection to a single account and is useful when cost control or selective coverage is required. After enabling at the resource level, the subscription-level Environment settings page will still show Defender for Storage as Off — this is expected. The subscription plan and resource-level overrides are independent. Confirm protection status on the storage account's own Microsoft Defender for Cloud blade, not at the subscription level.

  7. Confirm the protection status shows On after enabling.


Confirm diagnostic logging

Diagnostic logs capture management-plane operations on the storage account — including configuration changes, access policy modifications, and Defender for Storage alerts — and forward them to a Log Analytics workspace for retention and querying.

  1. In the left menu for the storage account (<storage-account-name>), under Monitoring, select Diagnostic settings.

    The page shows diagnostic settings for the storage account and its sub-services. StorageRead, StorageWrite, and StorageDelete are blob-level log categories, so you need to configure settings at the blob sub-service level.

  2. Under the resource list, select blob.

  3. Select + Add diagnostic setting.

  4. Configure the following:

    Setting Value
    Diagnostic setting name sc500-storage-diag
    Logs Select StorageRead, StorageWrite, and StorageDelete
    Destination Select Send to Log Analytics workspace
    Subscription Your lab subscription
    Log Analytics workspace sc500-lab2a-log

    Note: The page may also show a Diagnostic settings (classic) row with a status of Not enabled. Ignore this row — it is the legacy configuration model. The + Add diagnostic setting button above it uses the correct Azure Monitor version.

  5. Select Save.

    Note: Log ingestion to Log Analytics typically takes 2–5 minutes after the diagnostic setting is saved. In a production environment, you would query this workspace using KQL to review access patterns, detect configuration drift, and correlate storage activity with Defender for Storage alerts.


Summary

In this lab, you secured an Azure storage account that was previously open to all networks with no threat detection.

You created a stored access policy on the training-data container and generated a SAS token that references it. Unlike standalone SAS tokens — which cannot be revoked without rotating the account key — a policy-backed token can be invalidated instantly by modifying or deleting the policy. You verified the token worked before any network restrictions were in place.

You then changed the storage account from Allow all networks to a VNet-only configuration, restricting data-plane access to the sc500-lab2a-vnet subnet and removing the Allow trusted Microsoft services exception. You confirmed the restriction is active by running the same blob read request from Cloud Shell and observing the 403 error — and by observing that the Azure portal's Storage browser was also blocked from your current IP.

Finally, you enabled Defender for Storage at the resource level to detect anomalous access patterns and data exfiltration attempts, and configured diagnostic log forwarding to a Log Analytics workspace for audit retention.