Developing Cloud Applications with Windows Azure Storage: Blobs

  • 3/15/2013

Performing create, read, update, and delete blob operations

Blobs contain many operations for saving and retrieving data to and from storage. You’ll begin with the simple operation of creating a new blob container and populating it with your first blob.

Blob container security

It is useful for you to organize your blobs into storage containers by grouping data with the same security requirements into the same containers (or sets of identically secured containers, as may be appropriate). This strategy should include grouping blobs that your application requires anonymous (public) read-only access to (which is our next topic). Because each blob can be referenced directly from the Internet using its URI, delivery of anonymous public read-only content to web browsers is one of the most useful purposes of blob storage. If blobs in the same container have differing security requirements, you probably want to re-factor your design until they don’t. Blob containers are full-access when the request is made with the Windows Azure account key or public read-only (where anyone with the URL to the blob or blob container can read its contents and its metadata), or they might be more granular when the request is made with a Shared Access Signature. Each of these security models is covered in this chapter.

The Windows Azure account key should generally be kept secret, because it’s really the key to the entire data fiefdom controlled by a single Windows Azure data storage account. Your application using the account key is similar to Microsoft SQL Server using an account with database owner authority. This trusted application model is generally adequate for many on-premise applications and services. However, you may want to give some attention and analysis to the security ramifications of using the trusted application model in your cloud architectures. The risks go up considerably when you’re no longer operating behind the safety and protection of your corporate firewall, where identities are managed and under the careful control and scrutiny of your corporate personnel department, IT staff, and infrastructure team. You may also want to give some thought to using different storage accounts for different applications (or sets of applications) in order to compartmentalize your data so that the leak of one application’s credentials is not a threat to the data of other applications.

Anonymous (public) read-only blob access

In the business use-case section of this chapter, I suggested that blob web content could be placed in Windows Azure blob storage, which the markup code could simply reference. To enable this scenario, the content must be publicly accessible via an unauthenticated web request. Most content on the web is public read-only data, but by default, blob containers do not allow public access, so to enable this business use-case, you have to set your permissions on your blob containers to grant the desired level of access to anonymous users. Blob storage is the only type of data storage in Windows Azure that allows public read-only access. (Unauthenticated public access is not available for Windows Azure table or queue storage.)

By default, no public access is granted to a blob container or the blobs it encapsulates. You will learn later in this chapter how you can change this setting to Blob to allow public access to individual blobs stored in the container or to Container, which grants public read-only access to the blob container and all the blobs contained therein. It is not possible to set public read-only access on an individual blob—only on its container.

Creating the blob container

You can create a new blob container named demo by sending an HTTP PUT request to the URI of the blob container location. The following request creates a new container called demo in the azureinsiders storage account.

PUT http://azureinsiders.blob.core.windows.net/demo?restype=container&timeout=90 HTTP/1.1
x-ms-version: 2012-02-12
User-Agent: WA-Storage/2.0.0
x-ms-date: Mon, 17 Dec 2012 05:32:31 GMT
Authorization: SharedKey azureinsiders:+TRYhpqkDgZ6WlgG37l0qa+d/5tfvZXyYqpEKjaDs9w=
Host: azureinsiders.blob.core.windows.net
Content-Length: 0

The preceding code results in an HTTP status code 201 (Created) upon its successful completion.

HTTP/1.1 201 Created
Transfer-Encoding: chunked
Last-Modified: Mon, 17 Dec 2012 05:32:31 GMT
ETag: "0x8CFAA2F0B3FF8C8"
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: 79eea64c-7f01-4193-a6a5-1d918868dac8
x-ms-version: 2012-02-12
Date: Mon, 17 Dec 2012 05:32:31 GMT
0

In the Wintellect.DevCloudAppsAzureStorage project in the sample code, locate the Blob-Patterns.Basics method in StoragePatterns.cs. The Basics method, part of which is shown in the following code, accepts a CloudStorageAccount, which is a container around security credentials and storage endpoint addresses. You create a container named demo if one doesn’t already exist.

// Use an OperationContext for debugging and to estimate billing
OperationContext oc = new OperationContext();
oc.SendingRequest += (Object s, RequestEventArgs e) => {
     HttpWebRequest request = e.Request;
};
oc.ResponseReceived += (Object s, RequestEventArgs e) => {
    HttpWebRequest request = e.Request;
    HttpWebResponse response = e.Response;
    RequestResult rr = e.RequestInformation;
};

CloudBlobClient client = account.CreateCloudBlobClient();

// Create a container:
CloudBlobContainer container = client.GetContainerReference("demo");
Boolean created = container.CreateIfNotExists(null, oc);

Listing storage account containers

After the blob container demo has been created in the storage account, you should be able to see it in storage. The following HTTP request against the azureinsiders storage account augments the URI with the query string parameter ?comp=list, which in turn causes Windows Azure storage service to return a list of all blob containers for the storage account specified by the URI.

GET http://azureinsiders.blob.core.windows.net/?comp=list&timeout=90 HTTP/1.1
x-ms-version: 2012-02-12
User-Agent: WA-Storage/2.0.0
x-ms-date: Mon, 17 Dec 2012 05:55:22 GMT
Authorization: SharedKey azureinsiders:7QR/PWux3s6anFSQR2gSV5UPUvPInEuh+jeO8R3sflk=
Host: azureinsiders.blob.core.windows.net

The preceding code returns a response containing an enumeration of blob containers for the storage account.

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/xml
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: 7e1a5018-0080-472a-b817-33df3bb82f09
x-ms-version: 2012-02-12
Date: Mon, 17 Dec 2012 05:55:21 GMT

2F5
<?xml version="1.0" encoding="utf-8"?>
  <EnumerationResults AccountName="http://azureinsiders.blob.core.windows.net/">
    <Containers>
      <Container>
        <Name>demo</Name>
        <Url>http://azureinsiders.blob.core.windows.net/demo</Url>
        <Properties>
          <Last-Modified>Mon, 17 Dec 2012 05:32:31 GMT</Last-Modified>
          <Etag>"0x8CFAA2F0B3FF8C8"</Etag>
          <LeaseStatus>unlocked</LeaseStatus>
          <LeaseState>available</LeaseState>
        </Properties>
      </Container>
      <Container>
        <Name>manyblobs</Name>
        <Url>http://azureinsiders.blob.core.windows.net/manyblobs</Url>
        <Properties>
          <Last-Modified>Tue, 05 Jun 2012 00:14:02 GMT</Last-Modified>
          <Etag>"0x8CF10C73F0E2A12"</Etag>
          <LeaseStatus>unlocked</LeaseStatus>
          <LeaseState>available</LeaseState>
        </Properties>
      </Container>
    </Containers>
  <NextMarker />
</EnumerationResults>
0

If you’re using the Windows Azure storage client, the ListContainers method of the storage client will return an IEnumerable<CloudBlobContainer>.

// Show this account's containers:
foreach (var c in client.ListContainers(null, ContainerListingDetails.None,
operationContext: oc))
    Console.WriteLine(c.Uri);

At this point, you have an empty blob container. The following code creates and populates two empty blobs in that blob container.

// Create 2 blobs in the container:
CloudBlockBlob blob = container.GetBlockBlobReference("SomeData.txt");
using (var stream = new MemoryStream(("Some data created at " + DateTime.Now).Encode())) {
    blob.UploadFromStream(stream, operationContext: oc);
}
using (var stream = new MemoryStream()) {
    blob.DownloadToStream(stream, operationContext: oc);
    stream.Seek(0, SeekOrigin.Begin);
    Console.WriteLine(new StreamReader(stream).ReadToEnd());   // Read the blob data back
}

With the blob container and blobs created, you are ready to explore permissions settings.

Setting blob container permissions

Container permissions control public read-only access (public access) to a blob container and the blobs it contains. It is not possible to set public access permission on an individual blob. This permission is applicable only to blob containers. An individual blob inherits its public access characteristic by virtue of the container’s permission. To control public access to the blob container and its contents, perform an HTTP PUT operation against the URI of the blob container, setting the x-ms-blob-public-access header to one of three values listed in Table 5-1. (Note that the header is all lowercase letters, whereas the object model of the API depicted in Table 5-1 is Pascal-cased.) A value of container grants anonymous public read-only access to a blob container and its contents, a value of blob grants the same access but only to the blobs in the container, and a value of off prohibits any anonymous access.

In the following example, you are indicating that public access is being granted to the container and all of the blobs that it may contain.

PUT http://azureinsiders.blob.core.windows.net/demo?
    restype=container&comp=acl&timeout=90 HTTP/1.1
x-ms-version: 2012-02-12
User-Agent: WA-Storage/2.0.0
x-ms-blob-public-access: container
x-ms-date: Mon, 17 Dec 2012 06:54:11 GMT
Authorization: SharedKey azureinsiders:NYvUlXRWqZCFXhtPQu/o80FiKe8aKOlOSXAbHeyEOUY=
Host: azureinsiders.blob.core.windows.net
Content-Length: 62

<?xml version="1.0" encoding="utf-8"?><SignedIdentifiers />

Successful execution of the preceding HTTP PUT request will result in an HTTP status code 200 (OK).

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Last-Modified: Mon, 17 Dec 2012 06:54:11 GMT
ETag: "0x8CFAA3A745859B4"
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: 54cea7b5-26b7-444c-b3a5-10a251e779ad
x-ms-version: 2012-02-12
Date: Mon, 17 Dec 2012 06:54:10 GMT

Public access may be granted through the Windows Azure client library, too. A BlobContainerPermissions object is used to control public access to a blob container and the blobs it contains. An instance of BlobContainerPermissions has a PublicAccess property, which can be set to one of three values, as shown in Table 5-1.

Table 5-1 Blob container public access permission settings

Setting

Public read-only access

Off

Grant no public access to the container or the blobs stored in the container.

Blob

Grant public read-only access to all of the blobs stored in the container, but not to the container itself.

Container

Grant public read-only access to the container and all of the blobs stored in the container.

To grant public access permissions to the blob container and its blob contents, you create an instance of BlobContainerPermissions and set its PublicAccess property to BlobContainerPublicAccessType.Container. You then call the container’s SetPermissions method, passing in the permission object. There are three values to the BlobContainerPublicAccessType: Off (the default) prohibits public read-only access to the container and its blobs; Blob grants access to the blobs in the container (but not the container itself); and Container grants access to read the container and the blobs it encapsulates. The next bit of code illustrates this.

// Change container's security to allow read access to its blobs:
BlobContainerPermissions permissions = new BlobContainerPermissions {
    PublicAccess = BlobContainerPublicAccessType.Container
};
container.SetPermissions(permissions, operationContext: oc);

// Attempt to access a blob from browser & in code (succeeds):
Process.Start("IExplore", container.Uri.ToString()).WaitForExit();
using (var stream = new MemoryStream()) {
    anonymous.GetContainerReference("demo").GetBlockBlobReference("SomeData.txt")
       .DownloadToStream(stream, operationContext: oc);
    Console.WriteLine("Download result: " + stream.GetBuffer().Decode());
    Console.WriteLine();
}

// Show the container's blobs via REST:
Process.Start("IExplore", container.Uri + "?comp=list").WaitForExit();

The blob container demo has the default public read-only permission of off, meaning that any attempt to read the blobs located in this container without credentials will fail. To test this assertion and prove this point, you launch a web browser to the URL of the blob you uploaded. You then attempt to access one of the blobs using code to demonstrate that this also fails.

// Change container's security to allow read access to its blobs:
BlobContainerPermissions permissions = new BlobContainerPermissions {
    PublicAccess = BlobContainerPublicAccessType.Container
};
container.SetPermissions(permissions, operationContext: oc);

// Attempt to access a blob from browser & in code (succeeds):
Process.Start("IExplore", container.Uri.ToString()).WaitForExit();
using (var stream = new MemoryStream()) {
    anonymous.GetContainerReference("demo").GetBlockBlobReference("SomeData.txt")
       .DownloadToStream(stream, operationContext: oc);
    Console.WriteLine("Download result: " + stream.GetBuffer().Decode());
    Console.WriteLine();
}

    // Show the container's blobs via REST:
    Process.Start("IExplore", container.Uri + "?comp=list").WaitForExit();

Now when you launch Windows Internet Explorer on the blob’s URL, the contents of the blob are displayed. Because the container is now set to allow public read access, you can launch a browser directly against the blob container’s URL, passing the filtration criteria (?comp=list) in the query string to list the contents of the blob container, as shown in Figure 5-3.

Figure 5-3

Figure 5-3 A list of the blob container contents is shown here.

You’re going to set the PublicAccess property of the blob container to BlobContainerPublicAccessType.Off in the code that follows. This step may seem superfluous because Off is the default setting, but you want to ensure clarity of the demonstration, and you also want to ensure that the resulting access you observe is verifiably a result of the Shared Access Signature (SAS) and not a side-effect of anonymous access being granted. (You learn more about SAS in the next sections.) You use the SetPermissions method of the blob container to apply the BlobContainerPermission object to remove public access to the blob container and its contents. As mentioned earlier, this is equivalent to executing an HTTP PUT operation against the URI of the blob container with the x-ms-blob-public-access header set to a value of off. Attempting to show the contents of the container or blob in a web browser after applying the permissions results in an HTTP 403 (Forbidden) error code, which proves that no anonymous access is allowed to your blob container. The following code demonstrates this.

container.SetPermissions(new BlobContainerPermissions {
   PublicAccess = BlobContainerPublicAccessType.Off });
CloudBlob blob = container.GetBlobReference("test.txt");
blob.UploadText("Data accessed!");
// This fails
Process.Start("IExplore", blob.Uri.ToString()).WaitForExit();