Azure DevOps: white list Azure Pipeline IP in Cosmos database firewall. How to add the Azure DevOps Hosted Agent IP address to a Cosmos database firewall.

I am currently doing the Azure backup strategy for one of our customers. While Azure takes regular backups of the Cosmos databases, in case of an application failure that would corrupt the data, they would not help. Because Azure would back up the already corrupted data.

The solution is to store our own backups. We use an Azure Pipeline which takes a daily backup of our databases.

But the problem is that the databases are IP restricted behind a firewall. And the Azure Pipeline fails because the Azure DevOps Hosted Agent cannot pass through the firewall.

The solution found is to add a step in our Azure Pipeline, which adds the Azure DevOps Agent IP address to the database white list and removes it at the end.

PowerShell script:

#Function to add current IP to database account
Function add-ip-databaseaccount ($customIP, $databaseResourceGroup, $databaseAccount){
    Write-Host "  " $customIP "is not allowed. Adding it now.." -ForegroundColor Green
    $databaseAccountIpRangeFilter = (Get-AzResource -Name $databaseAccount -ExpandProperties).Properties.ipRangeFilter
    $databaseAccountIpRangeFilter = $databaseAccountIpRangeFilter + "," + $customIP
    $databaseAccountProperties = @{"databaseAccountOfferType"="Standard"; "ipRangeFilter"=$databaseAccountIpRangeFilter}
    Set-AzResource -ResourceType "Microsoft.DocumentDb/databaseAccounts" -ResourceGroupName $databaseResourceGroup -Name $databaseAccount -Properties $databaseAccountProperties -Force
    Get-AzResource -Name $databaseAccount -ExpandProperties
    #Although the IP is being added to the allowed list, it takes some time until the changes are being used
    Start-Sleep -Seconds 600
}

#Function to remove current IP to database account
Function remove-ip-databaseaccount ($customIP, $databaseResourceGroup, $databaseAccount){
    $databaseAccountIpRangeFilter = (Get-AzResource -Name $databaseAccount -ExpandProperties).Properties.ipRangeFilter
    $databaseAccountIpRangeFilter = $databaseAccountIpRangeFilter.Split(',')
    Write-Host Checking firewall settings for database $databaseAccount :
    if ($customIP -in $databaseAccountIpRangeFilter) {
        Write-Host "  " $customIP "is allowed. Removing it now.." -ForegroundColor Green
        [System.Collections.ArrayList]$ArrayList = [array]$databaseAccountIpRangeFilter
        $ArrayList.Remove($ArrayList[$ArrayList.IndexOf($customIP)])
        $databaseAccountIpRangeFilter = $ArrayList -join ','
        $databaseAccountProperties = @{"databaseAccountOfferType"="Standard"; "ipRangeFilter"=$databaseAccountIpRangeFilter}
        Set-AzResource -ResourceType "Microsoft.DocumentDb/databaseAccounts" -ResourceGroupName $databaseResourceGroup -Name $databaseAccount -Properties $databaseAccountProperties -Force
        Get-AzResource -Name $databaseAccount -ExpandProperties
    }
    else {
        Write-Host "  " $customIP "was already not allowed." -ForegroundColor Green
    }
}

#######################################################################

#Retrieve the current IP
$ipinfo = Invoke-RestMethod http://ipinfo.io/json
$myip = $ipinfo.ip

#Check if DB has IP filtering active:
$dbAccountIpRangeFilter = (Get-AzResource -Name $dbAccount -ExpandProperties).Properties.ipRangeFilter

#If IP filtering is in place
if($dbAccountIpRangeFilter)
{
    Write-Host "IP filtering is active for " $dbAccount
    $dbAccountIpRangeFilter = (Get-AzResource -Name $dbAccount -ExpandProperties).Properties.ipRangeFilter
    $dbAccountIpRangeFilterArray = $dbAccountIpRangeFilter.Split(',')
    Write-Host Checking firewall settings for db $dbAccount :

    if ($myip -in $dbAccountIpRangeFilterArray) {
        Write-Host "  " $myip "is already allowed." -ForegroundColor Green
        backup-db $dbResourceGroup $dbAccount $dbName $dbCollections
    }
    else{
        add-ip-dbaccount $myIp $dbResourceGroup $dbAccount
        backup-db $dbResourceGroup $dbAccount $dbName $dbCollections
        remove-ip-dbaccount $myIp $dbResourceGroup $dbAccount    
    }
}
else
{
    Write-Host "IP filtering is not active for " $dbAccount
    backup-db $dbResourceGroup $dbAccount $dbName $dbCollections
}

I will try to explain the function backup-database in a separate blog post.

Microsoft Azure Portal App for Windows, iPhone and Android

Microsoft has released a preview version of the Azure Portal app for Windows. I used it for some time now and it works quite well. You get rid of the browser, while the functionalities remain the same as in the Azure portal.

Download for Windows: https://portal.azure.com/App/Download

When on the go, I recommend the Microsoft Azure app for iOS or Android.

App Store Download: https://itunes.apple.com/app/microsoft-azure/id1219013620?ls=1&mt=8

Google Play Download: https://play.google.com/store/apps/details?id=com.microsoft.azure

Azure Serverless Architectures: host a static website in Azure Storage

Azure Storage v2 accounts allow you to serve static content (HTML, CSS, JavaScript, and image files) directly from a storage container named $web. Taking advantage of hosting in Azure Storage allows you to use serverless architectures including Azure Functions and other PaaS services.

When you enable static website hosting on your storage account, you select the name of your default file and optionally provide a path to a custom 404 page. As the feature is enabled, a container named $web is created if it doesn’t already exist.

You can enable static website from the Azure CLI using this script:

#Parameters

param(
    [Parameter(Mandatory=$True, HelpMessage="Please specify the storage account name")][String]$Storage_Name_Web,
    [Parameter(Mandatory=$True, HelpMessage="Please specify the subscription ID")][String]$subscriptionId
)

#Connect to Azure
Connect-AzAccount

#Change the context to the Azure subscription
Set-AzContext -Subscription $subscriptionId

# Enable static website
az account set --subscription $subscriptionId
az storage blob service-properties update --account-name $Storage_Name_Web --static-website --404-document "404.html" --index-document "index.html"

Or you can enable static website from the Azure Portal:

To test the website, you can upload a sample html file to the blob storage. From the storage account, go to the blobs section and expand the $web container.

Upload a sample html file with the name “index.html”.

In my case the index.html contains a link to a picture:

<html>
  <body>
    <img src="blog.bmp">
  </body>
</html>

Now when you browse the blob storage endpoint, in my case
https://ranariwebstorage.z6.web.core.windows.net , you get the index.html:

How to create a Dynamics 365 trial tenant

It takes you 8 minutes to create an Office 365 tenant for testing purposes.

Here is how you can do it.

  • Confirm your order. You do not need a credit card.
  • Go to the Billing > Subscriptions page to confirm that the Dynamics 365 subscription has been added:
  • You need to assign the Dynamics 365 license to your users.
  • In the O365 Admin Center go to Users > Active users.
  • Select the user(s) and click Edit next to Product licenses.
  • Enable the Dynamics 365 Customer Engagement Plan licenses and click Save.

How to create an Office 365 trial tenant

It takes you 5 minutes to create an Office 365 tenant for testing purposes.
Here is how you can do it.

  • Scroll to the bottom of the page and click on more details.
  • Click “Try for free” in the Enterprise E3 or E5 column.
  • Fill in your contact data and click Next:
  • Type in a user ID and password to create your used ID and click Create my account:

Note: choose your tenant name carefully (ranari in my case).

You will not be able change the tenant name later. SharePoint and other services will use this domain name e.g. https://ranari.sharepoint.com and you will not be able to change it.

You can check if the tenant name is available on the site: https://o365.rocks/

  • Enter your phone number to prove that you are not a robot and enter the verification code received.
  • You’re ready to go.

Create Azure Cosmos Databases programmatically using ARM templates and PowerShell

I have recently deployed tens of Azure resources programmatically. While most of the Azure resources and settings you can define in the ARM templates in JSON format, there are scenarios where you need to benefit from the power of PowerShell and the Azure CLI.

In this article I will demonstrate how to deploy an Azure Cosmos DB Account using an ARM template and then create the databases and collections using PowerShell.

To get the Azure templates for Visual Studio, install the Azure development SDK:

Create in Visual Studio a new project from type Azure Resource Group:

Edit the file azuredeploy.json like this:

{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": {
"type": "string",
"defaultValue": "ranari"
}
},
"variables": {},
"resources": [
{
"apiVersion": "2015-04-08",
"name": "[concat(parameters('environmentName'),'mongodb')]",
"type": "Microsoft.DocumentDB/databaseAccounts",
"kind": "MongoDB",
"location": "[resourceGroup().location]",
"scale": null,
"properties": {
"ipRangeFilter": "",
"enableAutomaticFailover": false,
"enableMultipleWriteLocations": false,
"isVirtualNetworkFilterEnabled": false,
"virtualNetworkRules": [],
"databaseAccountOfferType": "Standard",
"consistencyPolicy": {
"defaultConsistencyLevel": "Session",
"maxIntervalInSeconds": 5,
"maxStalenessPrefix": 100
},
"locations": [
{
"locationName": "[resourceGroup().location]",
"failoverPriority": 0
}
],
"capabilities": []
},
"dependsOn": [],
"tags": {}
}
],
"outputs": {}
}

Deploy the solution by following these steps:

Browse the Azure site and observe that the Azure Cosmos DB Account has been created:

At this point the Azure Cosmos DB account is empty. To add a database and collection, run following PowerShell script:

#####################################################

#Parameters & variables

param(
[Parameter(Mandatory=$true)][String]$environmentName,
[Parameter(Mandatory=$false)][String]$subscriptionId = "e9c6149c-f5f6-44c7-aa8b-b8d2fa7f6140"
)

$resourceGroup = $environmentName.ToLower() + "-databases"
$mongoDBAccount = $environmentName.ToLower() + "mongodb"
$mongoDBdatabase = $environmentName.ToLower() + "-cars"

$mongoDBdatabaseCollections=@("VW",
"BMW",
"Mercedes",
"Audi",
"Skoda",
"Renault",
"Ford")

#####################################################

#Connect to Azure
Connect-AzAccount
az account set --subscription $subscriptionId

#####################################################
#Create databases and collections

az cosmosdb database create --resource-group-name $resourceGroup --db-name $mongoDBdatabase --name $mongoDBAccount

foreach($collection in $mongoDBdatabaseCollections)
{
az cosmosdb collection create --resource-group-name $resourceGroup --name $mongoDBAccount --db-name $mongoDBdatabase --collection-name $collection --throughput 400
}
#####################################################

Now check in Azure that the database and collection were successfully created:

Office 365: SharePoint classic lists and libraries shifted automatically to modern as a result of tenant opt-out starting April 2019

Starting April 1, 2019, it will no longer be possible to restrict an entire organization (tenant) to classic mode for lists and libraries. Lists and libraries may still use classic mode using the granular opt-out switches that we provide at the site collection, site, list, and library levels. Additionally, lists that use certain features and customizations that are not supported by modern will still be automatically switched to classic mode.

After April 1, lists and libraries that are in classic mode as a result of tenant opt-out will automatically be shifted to modern. Users will benefit from enhancements and new features such as attention views, PowerApps integration, Flow, column formatting, and the filters pane.

What should I do to prepare for this change?
The SharePoint Modernization scanner identifies sites and lists that have customizations that are not supported by modern UI. Although many of these lists will automatically remain in classic mode even after this change, you may wish to keep some sites running entirely in classic to avoid users switching between different experience modes within a single site.

Administrators can use a PowerShell script to enable or disable the modern experience for a single site collection or for a list of site collections as provided by the SharePoint Modernization scanner. Alternately, users can still use the “return to classic” option on modern views of lists or libraries to temporary return to classic, and list owners can use List Settings to configure that list to use the classic experience for all users.

PowerShell scripts to opt out of the modern list and library experience: https://docs.microsoft.com/en-us/sharepoint/dev/transform/modernize-userinterface-lists-and-libraries-optout

Source: Office 365 Message Center