Archive for category Azure

Azure Key Vault Access Policies

Azure-KeyVault_COLORAzure Key Vault is used to safeguard system critical data, such as keys, secrets and certificates that are part of your infrastructure core operations.

Key Vault access is managed through two interfaces: management plane – this dictates access to the Key Vault itself but not managing its content which is based on RBAC model, data plane – this enables granular control of keys/secrets/certificates, which relies on Access Policy.

This brings us nicely to Access Policies which is meat of this article. I have had to deal with a situation lately while applying access policy for a colleague while a restriction is imposed on my Azure AD account (see example below)

 

AKV1

AKV2

This would have limited access to my account (which is external) to AAD, which means I cant read or search AAD which is required when adding access policy via the GUI – Setting RBAC without access to AAD works (as an exception) if you know the full FQDN of the user you are adding.

The way around is to use PowerShell, but this requires a prerequisite which is the Azure AD object ID for the user you want to add, this can be achieved by adding them temporarily on management plane to access their user properties and copy object ID before deleting that rule.

Now to the command I used to apply that permission:

Set-AzKeyVaultAccessPolicy -ObjectId *** -VaultName **** -PermissionsToKeys **** -PermissionsToSecrets **** -PermissionsToCertificates **** -BypassObjectIdValidation 

 

Key property here is the BypassObjectIdValidation, if the Object ID exists in AAD then it will be linked and the user will have their access policy applied to Azure Key Vault.

, , ,

Leave a comment

Azure Custom Script Extension

It has been a while since I had the opportunity to blog. It has been a very busy period which is good in some ways …

Anyway, today I wanted to pick up on Azure CSE. Azure CSE is beneficial in many ways when it comes to configuring VM’s, installing application and making app configuration changes.

Normally, when running an Azure CSE for Windows, people do favour Powershell scripts which gives them the power (in the name) and functionality to do their configuration elements with ease.

The recent issue I faced with Powershell scripts are particularly around Invoke-Command which runs a script block inside the VM under VM System context. Those Invoke-Command statements were issued to install a sequence of applications and configure the app in a certain way.

My word of advice is to avoid using Invoke-Command and use Start-Process instead.

For example:

Invoke-Command -ScriptBlock { “C:\Temp\Setup.exe /q”}

You would think this would go and install that Setup.exe program, but it many cases it doesn’t and you find there is a missing element in your application sequence.

A better and more effective way of doing this is:

Start-Process “C:\Temp\Setup.exe” -ArgumentList “/q” -Wait

This would ensure running of the Setup.exe installer and waiting for finish flag before it continues with the rest of your Azure CSE script, which the Invoke-Command doesn’t offer.

Simple things like that would make you script neater especially in managing error/failure codes during the install of the application.

To be honest, I would prefer the DSC way if you have the time and knowledge.

, ,

Leave a comment

Windows 2016 with Containers

Windows 2016 has a feature to add Windows and Hyper-V containers, both with their own advantages and limitations but I am not going to go over that in detail here. Below is a diagram that shows the architecture of each implementation and as we can see straight away that the Kernel is shared when using Windows Server Containers, hence it can only run Windows based instructions. This implementation doesn’t provide any security boundaries between containers as it exposes instructions of a container to the host and to all other VM’s, I wont go to comparision.

Windowscontainer

What I wanted to go over is a recent deployment that I have gone through which experienced some unusual behaviours.

Using Windows Server 2016 with Containers image from Azure gallery, provisioned a new Windows 2016 host, but that host on completion was missing Host Network Service (HNS network adaptor), this adaptor will be used for any communication that is external to the host, for example accessing the Internet.

The host was showing that Docker host network was bound to an adaptor that wasn’t showing in Network Connections and as you know that we don’t have access to see the status of that network adaptor on the host VM.

Using commands below I managed to clear Docker network settings and bring that network adaptor back online.

Using Powershell ran under admin credentials:

Stop-service hns

Stop-service docker

Get-ContainerNetwork | Remove-ContainerNetwork -force

Get-NetNat | Remove-NetNat

Start-Service docker

 

Your containers should have Internet/External access.

 

Leave a comment

StorSimple on site appliance factory reset

StorSimpleWith a recent client, we had to re-register the StorSimple physical appliance to a new instance of StorSimple device manager instance, the old instance was deleted and no longer available on Microsoft Azure (ASM).

If you attempt to run Invoke-HcsSetupWizard on the appliance, it would error with the appliance is already registered to another instance and the only way to register it to a new instance is to factory reset the device.

While resetting, the device hang up on phase 3 and wouldn’t go past that stage. After speaking to Microsoft Support, they have advised to shutdown both controllers and re-attempt to run the factory reset again which was successful in this case.

Just keep in mind that you will be losing all your device configuration and any data that you have accumulated on the device after the reset, ensure you update device after the reset.

Leave a comment

DevTest Labs and Auto-start

DevTestIconLargeWhile working with DevTest Labs on Microsoft Azure, it’s a good idea to make sure you have Auto-start and Auto-shutdown policies to keep cost down on resource utilisation while not being used.

The mistake or misconception I had form customers and I see this continuously that they forget to opt the VM in or out of the policy when scheduling these machines to shutdown or start at certain times.

This needs to be enforced on a per VM basis, see below.

optin

, , ,

Leave a comment

Application Gateway bug – Jan 2017

bug-512I have been working with Microsoft lately on an issue that I was experiencing with an Azure application gateway (appGW) deployment that require both internal and external interfaces handling traffic over HTTPS.

If you try to attach the same AppGW front end port to internal and external front end configuration, this would cause the appGW to misbehave. In my scenario I had a rule attached to external interface for handling incoming traffic but no rule attached on the internal interface and as a consequence the internal interface started processing traffic while the external interface was rejecting all connections (not even 502 error! would you believe!). Just to note that all my appGW deployments are scripted using PowerShell/JSON.

Microsoft managed to replicate this internally and issued a bug report, they are working on it as we speak but without ETA currently.

I had to drop my second listener (internal) in order to bring the appGW back to it’s expected behaviour!

, , , ,

2 Comments

The myth of Azure Application Gateway – Part 2

5db46fc5-d9c1-44ae-a0f6-4ae1d2301395In part 1 of this article I have gone through creating Azure Applications Gateways (AGW) using Powershell which is a powerful way of deploying resources on Azure, using recursive functions and methods you could build a complex solution in few lines. Unlike Powershell, JSON is a static solution. It gives you a way of creating a baseline deployment, I still haven’t found a way of controlling sub-resources (which isn’t governed by the JSON Copy() function).

In this article I will go over the same deployment using a JSON template, this only serve as a reference point for your deployment. I will use the same Azure AGW configuration I used in part 1 to keep both deployments consistent.

When building Azure AGW, same prerequisites apply to the JSON method. There is a subtle difference which I will go over when I get to that point.

So this time I have prepared a Visio diagram to simplify our deployment and understand what we are trying to achieve – see below.

appgateway-blog

The diagram shows a Web server hosting four websites running a combination of HTTP HTTPS with authentication (using Basic authentication) or Anonymous access.

The only thing different in comparison to previous Powershell deployment is the steps we take to process SSL certificates and passing them over to the JSON template.

$pfxCert = Get-Content "pathToYourSSL.pfx" -Encoding Byte
$pfxCert = [System.Convert]::ToBase64String($pfxCert)
The above steps will ensure SSL cert is passed on based on Base 64 Binary which is what is expected by the JSON parameter.
As part of the deployment I have also created a custom probe that target an HTTPS site which accepts anonymous authentication, otherwise the default probe would fail causing the whole site to be unavailable. Custom probes are a good way in overcoming issues around sites that require NTLM or basic authentication (but not form based authentication).
Make sure your Application Gateway can talk to the backend server, if you have firewalls or even IIS Request Filtering could cause the connection to fail, ensure appropriate ports are allowed both ways.
So without further ado I have attached the full code below for your leisure ….
{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "applicationGatewayName": {
      "type": "string",
      "minLength": 1
    },
    "location": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "Region"
      }
    },
    "virtualNetworkName": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "Application Gateway vnet name"
      }
    },
    "virtualNetworkResourceGroup": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "Application Gateway vnet resource group name"
      }
    },
    "subnetName": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "Application Gateway subnet name"
      }
    },
    "publicIPAddressName": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "PIP resource name"
      }
    },
    "certData": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "SSL data in binary Base64 format"
      }
    },
    "certPassword": {
      "type": "securestring",
      "minLength": 1,
      "metadata": {
        "description": "SSL certificate password"
      }
    },
    "authCert": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "Public authentication certificate - in binary Base64 format"
      }
    },
    "Authhostname": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "Backend hostheader/Host SNI names used as part of your HTTPS request"
      }
    },
    "Noauthhostname": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "Backend hostheader/Host SNI names used as part of your HTTPS request"
      }
    },
    "backendIPAddress": {
      "type": "string",
      "minLength": 1,
      "metadata": {
        "description": "Backend web server IP address"
      }
    }
  },
  "variables": {
    "applicationGatewayID": "[resourceId('Microsoft.Network/applicationGateways',parameters('applicationGatewayName'))]",
    "capacity": 2,
    "skuName": "Standard_Medium",
    "subnetRef": "[concat(variables('vnetID'),'/subnets/',parameters('subnetName'))]",
    "vnetID": "[resourceId(parameters('virtualNetworkResourceGroup'),'Microsoft.Network/virtualNetworks',parameters('virtualNetworkName'))]"
  },
  "resources": [
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/publicIPAddresses",
      "name": "[parameters('publicIPAddressName')]",
      "location": "[parameters('location')]",
      "properties": {
        "publicIPAllocationMethod": "Dynamic"
      },
      "tags": {
        "displayName": "[parameters('publicIPAddressName')]"
      }
    },
    {
      "apiVersion": "2016-06-01",
      "name": "[parameters('applicationGatewayName')]",
      "type": "Microsoft.Network/applicationGateways",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]"
      ],
      "properties": {
        "sku": {
          "name": "[variables('skuName')]",
          "tier": "Standard",
          "capacity": "[variables('capacity')]"
        },
        "sslCertificates": [
          {
            "name": "appGatewaySslCert",
            "properties": {
              "data": "[parameters('certData')]",
              "password": "[parameters('certPassword')]",
              "publicCertData": "[parameters('authCert')]"
            }
          }
        ],
        "authenticationCertificates": [
          {
            "name": "PublicCert",
            "properties": {
               "data": "[parameters('authCert')]"
            }
          }

        ],
        "gatewayIPConfigurations": [
          {
            "name": "appGatewayIpConfig",
            "properties": {
              "subnet": {
                "id": "[variables('subnetRef')]"
              }
            }
          }
        ],
        "frontendIPConfigurations": [
          {
            "name": "appGatewayFrontendIP",
            "properties": {
              "PublicIPAddress": {
                "id": "[resourceId('Microsoft.Network/publicIPAddresses',parameters('publicIPAddressName'))]"
              }
            }
          }
        ],
        "frontendPorts": [
          {
            "name": "appGWFEHttps",
            "properties": {
              "Port": 443
            }
          },
          {
            "name": "appGWFEHttp",
            "properties": {
              "Port": 80
            }
          }
        ],
        "probes": [
          {
            "name": "CustomProbe",
            "properties": {
              "protocol": "Https",
              "host": "auth.simplesite.com:443",
              "path": "/noauth/iisstart.htm",
              "interval": 5,
              "timeout": 120,
              "unhealthyThreshold": 2
            }
          }
        ],
        "backendAddressPools": [
          {
            "name": "appGatewayBackendPool",
            "properties": {
              "BackendAddresses": [
                {
                  "IpAddress": "[parameters('backendIPAddress')]"
                }
              ]
            }
          }
        ],
        "backendHttpSettingsCollection": [
          {
            "name": "appGWBEHttpSettings",
            "properties": {
              "Port": 80,
              "Protocol": "Http",
              "CookieBasedAffinity": "Disabled"
            }
          },
          {
            "name": "appGWBEHttpsSettings",
            "properties": {
              "Port": 443,
              "Protocol": "Https",
              "CookieBasedAffinity": "Disabled",
              "probe": {
                "id": "[concat(variables('applicationGatewayID'), '/probes/CustomProbe')]"
              },
              "authenticationCertificates": [
                {
                  "id": "[concat(variables('applicationGatewayID'), '/authenticationCertificates/PublicCert')]"
                }
              ]
            }
          }
        ],
        "httpListeners": [
          {
            "name": "appGatewayHttpListener",
            "properties": {
              "FrontendIPConfiguration": {
                "Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]"
              },
              "FrontendPort": {
                "Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGWFEHttp')]"
              },
              "Protocol": "Http",
              "SslCertificate": null
            }
          },
          {
            "name": "appGatewayHttpsListenerNoAuth",
            "properties": {
              "FrontendIPConfiguration": {
                "Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]"
              },
              "FrontendPort": {
                "Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGWFEHttps')]"
              },
              "Protocol": "Https",
              "hostName": "[parameters('Noauthhostname')]",
              "SslCertificate": {
                "Id": "[concat(variables('applicationGatewayID'), '/sslCertificates/appGatewaySslCert')]"
              },
              "RequireServerNameIndication": "true"
            }
          },
          {
            "name": "appGatewayHttpsListenerAuth",
            "properties": {
              "FrontendIPConfiguration": {
                "Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]"
              },
              "FrontendPort": {
                "Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGWFEHttps')]"
              },
              "Protocol": "Https",
              "hostName": "[parameters('Authhostname')]",
              "SslCertificate": {
                "Id": "[concat(variables('applicationGatewayID'), '/sslCertificates/appGatewaySslCert')]"
              },
              "RequireServerNameIndication": "true"
            }
          }
        ],
        "requestRoutingRules": [
          {
            "Name": "HTTPrule",
            "properties": {
              "RuleType": "Basic",
              "httpListener": {
                "id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpListener')]"
              },
              "backendAddressPool": {
                "id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool')]"
              },
              "backendHttpSettings": {
                "id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGWBEHttpSettings')]"
              }
            }
          },
          {
            "Name": "HTTPSruleNoAuth",
            "properties": {
              "RuleType": "Basic",
              "httpListener": {
                "id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpsListenerNoAuth')]"
              },
              "backendAddressPool": {
                "id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool')]"
              },
              "backendHttpSettings": {
                "id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGWBEHttpsSettings')]"
              }
            }
          },
          {
            "Name": "HTTPSruleAuth",
            "properties": {
              "RuleType": "Basic",
              "httpListener": {
                "id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpsListenerAuth')]"
              },
              "backendAddressPool": {
                "id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool')]"
              },
              "backendHttpSettings": {
                "id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGWBEHttpsSettings')]"
              }
            }
          }
        ]
      }
    }
  ]
}
I know I haven’t written a detailed article around how each section of the JSON template code work together. I have tested this code in my own environment and I know it works for the web server I have for the backend. If you require help using the above template, please hit me with a message and I will try to clear some of the information around the code and usage cases.

5 Comments