// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Globalization; using System.IO; using System.Net; using System.Threading.Tasks; using System.Net.Http; using Microsoft.Build.Framework; using Task = Microsoft.Build.Utilities.Task; namespace Microsoft.DotNet.Build.CloudTestTasks { public sealed class CreateAzureContainer : Task { /// /// The Azure account key used when creating the connection string. /// [Required] public string AccountKey { get; set; } /// /// The Azure account name used when creating the connection string. /// [Required] public string AccountName { get; set; } /// /// The name of the container to create. The specified name must be in the correct format, see the /// following page for more info. https://msdn.microsoft.com/en-us/library/azure/dd135715.aspx /// [Required] public string ContainerName { get; set; } /// /// When false, if the specified container already exists get a reference to it. /// When true, if the specified container already exists the task will fail. /// public bool FailIfExists { get; set; } /// /// The read-only SAS token created when ReadOnlyTokenDaysValid is greater than zero. /// [Output] public string ReadOnlyToken { get; set; } /// /// The number of days for which the read-only token should be valid. /// public int ReadOnlyTokenDaysValid { get; set; } /// /// The URI of the created container. /// [Output] public string StorageUri { get; set; } /// /// The write-only SAS token create when WriteOnlyTokenDaysValid is greater than zero. /// [Output] public string WriteOnlyToken { get; set; } /// /// The number of days for which the write-only token should be valid. /// public int WriteOnlyTokenDaysValid { get; set; } public override bool Execute() { return ExecuteAsync().GetAwaiter().GetResult(); } public async Task ExecuteAsync() { Log.LogMessage( MessageImportance.High, "Creating container named '{0}' in storage account {1}.", ContainerName, AccountName); string url = string.Format( "https://{0}.blob.core.windows.net/{1}?restype=container", AccountName, ContainerName); StorageUri = string.Format( "https://{0}.blob.core.windows.net/{1}/", AccountName, ContainerName); Log.LogMessage(MessageImportance.Low, "Sending request to create Container"); using (HttpClient client = new HttpClient()) { Func createRequest = () => { DateTime dt = DateTime.UtcNow; var req = new HttpRequestMessage(HttpMethod.Put, url); req.Headers.Add(AzureHelper.DateHeaderString, dt.ToString("R", CultureInfo.InvariantCulture)); req.Headers.Add(AzureHelper.VersionHeaderString, AzureHelper.StorageApiVersion); req.Headers.Add(AzureHelper.AuthorizationHeaderString, AzureHelper.AuthorizationHeader( AccountName, AccountKey, "PUT", dt, req)); byte[] bytestoWrite = new byte[0]; int bytesToWriteLength = 0; Stream postStream = new MemoryStream(); postStream.Write(bytestoWrite, 0, bytesToWriteLength); req.Content = new StreamContent(postStream); return req; }; Func validate = (HttpResponseMessage response) => { // the Conflict status (409) indicates that the container already exists, so // if FailIfExists is set to false and we get a 409 don't fail the task. return response.IsSuccessStatusCode || (!FailIfExists && response.StatusCode == HttpStatusCode.Conflict); }; using (HttpResponseMessage response = await AzureHelper.RequestWithRetry(Log, client, createRequest, validate)) { try { Log.LogMessage( MessageImportance.Low, "Received response to create Container {0}: Status Code: {1} {2}", ContainerName, response.StatusCode, response.Content.ToString()); // specifying zero is valid, it means "I don't want a token" if (ReadOnlyTokenDaysValid > 0) { ReadOnlyToken = AzureHelper.CreateContainerSasToken( AccountName, ContainerName, AccountKey, AzureHelper.SasAccessType.Read, ReadOnlyTokenDaysValid); } // specifying zero is valid, it means "I don't want a token" if (WriteOnlyTokenDaysValid > 0) { WriteOnlyToken = AzureHelper.CreateContainerSasToken( AccountName, ContainerName, AccountKey, AzureHelper.SasAccessType.Write, WriteOnlyTokenDaysValid); } } catch (Exception e) { Log.LogErrorFromException(e, true); return false; } } } return true; } } }