Rename Azure Storage Blob using PowerShell

At the time of writing this there is no API to rename an Azure Storage blob in one operation. You have to copy the blob and delete the original one after the copy process completes.

You can vote for the feature here: Rename blobs without needing to copy them

Until then you can use my convenience Rename-AzureStorageBlob cmdlet:


function Rename-AzureStorageBlob
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
        [Microsoft.WindowsAzure.Commands.Common.Storage.ResourceModel.AzureStorageBlob]$Blob,

        [Parameter(Mandatory=$true, Position=1)]
        [string]$NewName
    )

  Process {
    $blobCopyAction = Start-AzureStorageBlobCopy `
        -ICloudBlob $Blob.ICloudBlob `
        -DestBlob $NewName `
        -Context $Blob.Context `
        -DestContainer $Blob.ICloudBlob.Container.Name

    $status = $blobCopyAction | Get-AzureStorageBlobCopyState 

    while ($status.Status -ne 'Success')
    {
        $status = $blobCopyAction | Get-AzureStorageBlobCopyState
        Start-Sleep -Milliseconds 50
    }

    $Blob | Remove-AzureStorageBlob -Force
  }
}

It accepts the blob as pipeline input so you can pipe the result of the Get-AzureStorageBlob to it and just provide a new name:

$connectionString= 'DefaultEndpointsProtocol=https;AccountName....'
$storageContext = New-AzureStorageContext -ConnectionString $connectionString

Get-AzureStorageBlob -Container 'MyContainer' -Context $storageContext -Blob 'myBlob.txt'|
    Rename-AzureStorageBlob -NewName 'MyNewBlob.txt'

You can also download the script from my GitHub repository.

Using Azure Key Vault in ASP.NET Core 2.0 with the options pattern

The best way to store secrets in your app is not to store secrets in your app

Almost every web application needs some kind of secrets like a SQL Database connection string or the primary key of a Storage Account in order to communicate with external services.

Certainly we don’t store these secrets within our source code since this would expose them to every developer that has access to the code. In Azure we could store the secrets within the Application Settings in the Azure Portal:

secret.PNG

But if a secret is used in multiple application and we need to change it (e. g. regenerate a storage account key) we would have to do that in multiple places. A better place to store secrets in Azure is the Key Vault.

Instead of storing each secret within our app we store them in the Key Vault and configure our app to access the secrets in the vault. Now we have a single place where we can manage our secrets.

Lets take a look how we can access those secrets in an ASP.NET Core 2.0 web application without introducing a dependency to Key Vault in the class that uses it. To create a vault, store secrets to it and create a service principal for the access policy see Get started with Azure Key Vault.

Our secret is stored in a class called ValueSettings:

public sealed class ValueSettings
{
	public string TestSecret { get; set; }
}

There is a ValuesController with one HttpGet method that returns our secret:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly ValueSettings _valueSettings;

    public ValuesController(IOptions<ValueSettings> valueSettings)
    {
        _valueSettings = valueSettings.Value;
    }

    [HttpGet]
    public IActionResult Get()
    {
        return Ok(_valueSettings.TestSecret);
    }
}

As you can see in line 6 the controller uses the options pattern to inject the actual settings. The controller doesn’t know where the secret is comming from and doesn’t have any dependencies to Azure Key Vault.

Now lets take a look how we need to configure our application for that. First we need to store the vault settings in our appsettings.json:

{
  "KeyVault": {
    "Vault": "https://myvault.vault.azure.net/",
    "ClientId": "myclientid",
    "ClientSecret": "myclientsecret"
  }
}

We also have a class that represents these settings:

public class KeyVaultSettings
{
    public string Vault { get; set; }
    public string ClientId { get; set; }
    public string ClientSecret { get; set; }
}

Now to configure the key vault we use the AddAzureKeyVault extension method in the Programm.cs:

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: false)
                    .AddEnvironmentVariables();

                var builtConfig = config.Build();
                var settings = builtConfig.GetSection("KeyVault").Get();

                config.AddAzureKeyVault(
                    settings.Vault, settings.ClientId, settings.ClientSecret);

            })
            .UseStartup()
            .Build();
}

And finally this is how our Startup looks like:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;   
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure(Configuration);

        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseMvc();
    }
}

You can download the complete example from my GitHub repository.

Web.config for hosting an Angular application on Azure Web App

If you host an Angular application on Microsoft Azure you probably want to define a mimemap for .json and .woff / .woff2 files to get rid of the console errors. Also in order to enable client side routing we have to add a rewrite rule.

This is how my web.config looks like:

<configuration>
    <system.webServer>
        <staticContent>
            <mimeMap fileExtension=".json" mimeType="application/json" />
            <remove fileExtension=".woff"/>
            <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
            <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
     </staticContent>

      <rewrite>
        <rules>
            <rule name="Angular" stopProcessing="true">
                <match url=".*" />
                <conditions logicalGrouping="MatchAll">
                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                </conditions>
                <action type="Rewrite" url="/" />
            </rule>
        </rules>
        </rewrite>
    </system.webServer>
</configuration>

To ensure the config gets deployed, put it in the src directory:

vs

And add it to list of assets within the .angular-cli.json:

cli

 

Determine whats wrong with your Azure Web / API App deployment

Sometimes a deployment goes wrong and instead of the actual app you get a message like this:

An error occurred while starting the application.
.NET Core 4.6.26020.03 X86 v4.0.0.0 | Microsoft.AspNetCore.Hosting version 2.0.1-rtm-125 | Microsoft Windows 10.0.14393 | Need help?

To see whats going wrong we have to enable logging:

  • Go to the Kudu environment (https://YOURSITE.scm.azurewebsites.net)
  • On the top menu, select Debug console -> PowerShell
  • Navigate to site -> wwwroot and open the web.config
  • Set the stdoutLogEnabled attribute to true and save the file. Example:
  • <?xml version="1.0" encoding="utf-8"?>
    <configuration>
    	<system.webServer>
    		<handlers>
    			<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
    		</handlers>
    		<aspNetCore processPath="dotnet" arguments=".\MyApp.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" />
    	</system.webServer>
    </configuration>
    
  • Create a folder named logs next to the web.config using mkdir logs

Now if you try to browse your site again, you should see a log file within the previously created directory containing additional information:

kudu