/// <summary> /// Processes the Push-CloudServices commandlet synchronously. /// </summary> protected override void ProcessRecord() { var azureSubscription = new AzureSubscription(SettingsPath, SubscriptionId); var azureCert = new X509Certificate2(Convert.FromBase64String(azureSubscription.ManagementCertificate)); var credentials = new CertificateCloudCredentials(SubscriptionId, azureCert); FlexStreams.UseThreadQueue(ThreadAdapter); using (EventStream.Subscribe(e => WriteObject(e.Message))) using (var computeClient = new ComputeManagementClient(credentials)) using (var storageClient = new StorageManagementClient(credentials)) { var targetSlot = VipSwap ? DeploymentSlot.Staging : DeploymentSlot.Production; var storageAccount = storageClient.CreateContainerIfNotExistsAsync( StorageAccountName, StorageContainer, BlobContainerPublicAccessType.Off, SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.List).Result; var blobClient = storageAccount.CreateCloudBlobClient(); var container = blobClient.GetContainerReference(StorageContainer); ProcessAsyncWork(ServiceNames.Select((t, i) => DeployCloudServiceAsync(computeClient, container, t, PackagePaths[i], ConfigurationPaths[i], DiagnosticsConfigurationPaths?[i], targetSlot)) .ToArray()); } WriteObject("Push-CloudServices completed!"); base.ProcessRecord(); }
/// <summary> /// Checks for the existence of a specific Azure Sql Firewall rule, if it doesn't exist it will create it. /// </summary> /// <param name="client">The <see cref="SqlManagementClient"/> that is performing the operation.</param> /// <param name="serverName">The name of the server that we want to use to create the database.</param> /// <param name="parameters">The <see cref="FirewallRuleCreateParameters"/> set of parameters for the rule.</param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task CreateFirewallRuleIfNotExistsAsync(this SqlManagementClient client, string serverName, FirewallRuleCreateParameters parameters) { Contract.Requires(client != null); Contract.Requires(!string.IsNullOrWhiteSpace(serverName)); Contract.Requires(parameters != null); Contract.Requires(!string.IsNullOrWhiteSpace(parameters.Name)); Contract.Requires(!string.IsNullOrWhiteSpace(parameters.StartIPAddress)); Contract.Requires(!string.IsNullOrWhiteSpace(parameters.EndIPAddress)); FirewallRuleGetResponse rule = null; FlexStreams.Publish(new CheckIfExistsEvent(AzureResource.FirewallRule, parameters.Name)); try { rule = await client.FirewallRules.GetAsync(serverName, parameters.Name); } catch (CloudException cex) { if (!cex.Error.Message.Contains($"Resource with the name '{parameters.Name}' does not exist")) { throw; } } if (rule != null) { FlexStreams.Publish(new FoundExistingEvent(AzureResource.FirewallRule, parameters.Name)); return; } await client.FirewallRules.CreateAsync(serverName, parameters); FlexStreams.Publish(new ProvisionEvent(AzureResource.FirewallRule, parameters.Name)); }
/// <summary> /// Checks for the existence of a specific Azure Sql Database, if it doesn't exist it will create it. /// </summary> /// <param name="client">The <see cref="SqlManagementClient"/> that is performing the operation.</param> /// <param name="serverName">The name of the server that we want to use to create the database.</param> /// <param name="databaseName">The name of the database we are creating.</param> /// <param name="databaseEdition">The edition of the database we are creating.</param> /// <param name="collationName">The database collation name.</param> /// <param name="sizeInGb">The maximum database size in GB.</param> /// <param name="createAppUser"></param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task CreateDatabaseIfNotExistsAsync( this SqlManagementClient client, string serverName, string databaseName, string databaseEdition, string collationName, int sizeInGb, bool createAppUser) { Contract.Requires(client != null); Contract.Requires(!string.IsNullOrWhiteSpace(serverName)); Contract.Requires(!string.IsNullOrWhiteSpace(databaseName)); Contract.Requires(!string.IsNullOrWhiteSpace(databaseEdition)); Contract.Requires(!string.IsNullOrWhiteSpace(collationName)); Contract.Requires(sizeInGb > 0 && sizeInGb <= 250); DatabaseGetResponse ns = null; FlexStreams.Publish(new CheckIfExistsEvent(AzureResource.SqlDatabase, databaseName)); try { ns = await client.Databases.GetAsync(serverName, databaseName); } catch (CloudException cex) { if (!cex.Error.Message.Contains($"Resource with the name '{databaseName}' does not exist")) { throw; } } if (ns != null) { FlexStreams.Publish(new FoundExistingEvent(AzureResource.SqlDatabase, databaseName)); return; } await client.Databases.CreateAsync( serverName, new DatabaseCreateParameters { Name = databaseName, Edition = databaseEdition, CollationName = collationName, MaximumDatabaseSizeInGB = sizeInGb, }); FlexStreams.Publish(new ProvisionEvent(AzureResource.SqlDatabase, databaseName)); if (!createAppUser) { return; } using (var adb = new DevOpsAzureDatabase(serverName, databaseName, FlexConfiguration.FlexSaUser, FlexConfiguration.FlexSaPwd)) { await adb.CreateDatabaseUserAsync(FlexConfiguration.FlexAppUser, FlexConfiguration.FlexAppUser, "dbo"); } }
/// <summary> /// Checks for the existence of a specific Azure Web Site, if it doesn't exist it will create it. /// </summary> /// <param name="client">The <see cref="ServiceBusManagementClient"/> that is performing the operation.</param> /// <param name="sbName">The name of the namespace we want to create.</param> /// <param name="region">The region where we want to create the namespace.</param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task CreateNamespaceIfNotExistsAsync(this ServiceBusManagementClient client, string sbName, string region) { Contract.Requires(client != null); Contract.Requires(!string.IsNullOrWhiteSpace(sbName)); Contract.Requires(!string.IsNullOrWhiteSpace(region)); ServiceBusNamespaceResponse sb = null; FlexStreams.Publish(new CheckIfExistsEvent(AzureResource.ServiceBus, sbName)); try { sb = await client.Namespaces.GetAsync(sbName); } catch (CloudException cex) { // TODO: At the moment of this writing, the ErrorCode in the exception was not in place, so it's a general catch with a contains and that's bad. // TODO: This needs to change to the proper trapping of the ErrorCode when they put this in place in the MAML. if (!cex.Message.Contains("Request to a downlevel service failed.")) { throw; } } if (sb != null) { FlexStreams.Publish(new FoundExistingEvent(AzureResource.ServiceBus, sbName)); return; } await client.Namespaces.CreateAsync(sbName, region); FlexStreams.Publish(new ProvisionEvent(AzureResource.ServiceBus, sbName)); }
/// <summary> /// Checks for the existence of a specific storage container, if it doesn't exist it will create it. /// </summary> /// <param name="client">The <see cref="StorageManagementClient"/> that is performing the operation.</param> /// <param name="accountName">The name of the storage account that we want to create the container in.</param> /// <param name="containerName">The name of the container that we are about to create.</param> /// <param name="publicAccess">The public access level for the container.</param> /// <param name="permissions">The set of permissions that the ACL for this container must have.</param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task <CloudStorageAccount> CreateContainerIfNotExistsAsync( this StorageManagementClient client, string accountName, string containerName, BlobContainerPublicAccessType publicAccess, SharedAccessBlobPermissions permissions) { Contract.Requires(client != null); Contract.Requires(!string.IsNullOrWhiteSpace(accountName)); Contract.Requires(!string.IsNullOrWhiteSpace(containerName)); Contract.Requires(containerName.Length >= 3); Contract.Requires(containerName.Length <= 63); var key = (await client.StorageAccounts.GetKeysAsync(accountName)).PrimaryKey; var storageAccount = CloudStorageAccount.Parse($"DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey={key}"); var container = storageAccount.CreateCloudBlobClient().GetContainerReference(containerName); await container.CreateIfNotExistsAsync(); await container.SetPermissionsAsync(new BlobContainerPermissions { PublicAccess = publicAccess }); FlexStreams.Publish(new ProvisionEvent(AzureResource.StorageContainer, containerName)); var acl = container.GetSharedAccessSignature(new SharedAccessBlobPolicy { Permissions = permissions }); FlexStreams.Publish(new StorageKeyEvent(accountName, containerName, acl)); return(storageAccount); }
/// <summary> /// Checks for the existence of a specific Azure Web Site, if it doesn't exist it will create it. /// </summary> /// <param name="client">The <see cref="WebSiteManagementClient"/> that is performing the operation.</param> /// <param name="webSpace">The name of the Web Space where the site should be.</param> /// <param name="parameters">The <see cref="WebSiteCreateParameters"/> that define the site we want to create.</param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task CreateWebSiteIfNotExistsAsync(this WebSiteManagementClient client, string webSpace, WebSiteCreateParameters parameters) { Contract.Requires(client != null); Contract.Requires(!string.IsNullOrWhiteSpace(webSpace)); Contract.Requires(parameters != null); WebSiteGetResponse service = null; FlexStreams.Publish(new CheckIfExistsEvent(AzureResource.WebSite, parameters.Name)); try { service = await client.WebSites.GetAsync(webSpace, parameters.Name, null); } catch (CloudException cex) { if (cex.Error.Code != "NotFound") { throw; } } if (service != null) { FlexStreams.Publish(new FoundExistingEvent(AzureResource.WebSite, parameters.Name)); return; } await client.WebSites.CreateAsync(webSpace, parameters); FlexStreams.Publish(new ProvisionEvent(AzureResource.WebSite, parameters.Name)); }
/// <summary> /// Checks for the existence of a specific Cloud Service, if it doesn't exist it will create it. /// </summary> /// <param name="client">The <see cref="ComputeManagementClient"/> that is performing the operation.</param> /// <param name="parameters">The <see cref="HostedServiceCreateParameters"/> that define the service we want to create.</param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task CreateServiceIfNotExistsAsync(this ComputeManagementClient client, HostedServiceCreateParameters parameters) { Contract.Requires(client != null); Contract.Requires(parameters != null); HostedServiceGetResponse service = null; FlexStreams.Publish(new CheckIfExistsEvent(AzureResource.CloudService, parameters.ServiceName)); try { service = await client.HostedServices.GetAsync(parameters.ServiceName); } catch (CloudException cex) { if (cex.Error.Code != "ResourceNotFound") { throw; } } if (service != null) { FlexStreams.Publish(new FoundExistingEvent(AzureResource.CloudService, parameters.ServiceName)); return; } await client.HostedServices.CreateAsync(parameters); FlexStreams.Publish(new ProvisionEvent(AzureResource.CloudService, parameters.ServiceName)); }
/// <summary> /// Checks for the existence of a specific storage container, if it doesn't exist it will create it. /// It also checks for a specific storage account that suits the system, if it doesn't exist in the subscription /// it will create it before attempting to create the container. /// </summary> /// <param name="client">The <see cref="StorageManagementClient"/> that is performing the operation.</param> /// <param name="model">The DevOpsFlex rich model object that contains everything there is to know about this database spec.</param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task CreateContainerIfNotExistsAsync(this StorageManagementClient client, AzureStorageContainer model) { Contract.Requires(client != null); Contract.Requires(model != null); Contract.Requires(model.System != null); string accountName; lock (StorageAccountGate) { FlexStreams.Publish(new CheckForParentResourceEvent(AzureResource.StorageAccount, AzureResource.StorageContainer, model.Name)); accountName = FlexConfiguration.StorageAccountChooser.Choose(client, model.System.StorageType.GetEnumDescription()).Result; StorageAccountGetResponse account = null; try { account = client.StorageAccounts.Get(accountName); } catch { // ignored } if (account == null) { accountName = (model.System.LogicalName + FlexDataConfiguration.StorageAccountString + FlexDataConfiguration.Branch).ToLower(); client.StorageAccounts.Create( new StorageAccountCreateParameters { Name = accountName, Location = model.System.Location.GetEnumDescription(), AccountType = model.System.StorageType.GetEnumDescription() }); FlexStreams.Publish(new ProvisionEvent(AzureResource.StorageAccount, accountName)); } else { accountName = account.StorageAccount.Name; FlexStreams.Publish(new FoundExistingEvent(AzureResource.StorageAccount, accountName)); } } await client.CreateContainerIfNotExistsAsync( accountName, FlexConfiguration.GetNaming <AzureStorageContainer>() .GetSlotName(model, FlexDataConfiguration.Branch, FlexDataConfiguration.Configuration), model.PublicAccess, model.Acl); }
/// <summary> /// Processes the Push-DevOpsFlexConfiguration commandlet synchronously. /// </summary> protected override void ProcessRecord() { FlexDataConfiguration.Branch = Branch; FlexDataConfiguration.Configuration = Configuration; FlexStreams.UseThreadQueue(ThreadAdapter); using (EventStream.Subscribe(e => WriteObject(e.Message))) using (var context = new DevOpsFlexDbContext(SqlConnectionString)) { ProcessAsyncWork(context.ProvisionAll(SubscriptionId, SettingsPath)); } WriteObject("Push-DevOpsFlexConfiguration completed!"); base.ProcessRecord(); }
/// <summary> /// Checks for the existence of a specific Azure Web Site, if it doesn't exist it will create it. /// </summary> /// <param name="client">The <see cref="WebSiteManagementClient"/> that is performing the operation.</param> /// <param name="model">The DevOpsFlex rich model object that contains everything there is to know about this web site spec.</param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task CreateWebSiteIfNotExistsAsync(this WebSiteManagementClient client, AzureWebSite model) { Contract.Requires(client != null); Contract.Requires(model != null); var webSpace = model.System.WebSpace.GetEnumDescription(); string webPlan; lock (HostingPlanGate) { FlexStreams.Publish(new CheckForParentResourceEvent(AzureResource.HostingPlan, AzureResource.WebSite, model.Name)); webPlan = FlexConfiguration.WebPlanChooser.Choose(client, webSpace).Result; if (webPlan == null) { var response = client.WebHostingPlans.Create(webSpace, new WebHostingPlanCreateParameters { Name = model.System.LogicalName + "-" + webSpace, NumberOfWorkers = 1, SKU = SkuOptions.Standard, WorkerSize = WorkerSizeOptions.Medium }); webPlan = response.WebHostingPlan.Name; FlexStreams.Publish(new ProvisionEvent(AzureResource.HostingPlan, webPlan)); } else { FlexStreams.Publish(new FoundExistingEvent(AzureResource.HostingPlan, webPlan)); } } await CreateWebSiteIfNotExistsAsync( client, webSpace, new WebSiteCreateParameters { Name = FlexDataConfiguration.GetNaming <AzureWebSite>().GetSlotName(model, FlexDataConfiguration.Branch, FlexDataConfiguration.Configuration), ServerFarm = webPlan }); }
/// <summary> /// Checks for the existence of a specific Azure Reserved IP, if it doesn't exist it will create it. /// </summary> /// <param name="client">The <see cref="NetworkManagementClient"/> that is performing the operation.</param> /// <param name="ipName">The name of the Reserved IP.</param> /// <param name="location">The Azure location of the Reserved IP.</param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task ReserveIpIfNotReservedAsync(this NetworkManagementClient client, string ipName, string location) { Contract.Requires(client != null); Contract.Requires(!string.IsNullOrWhiteSpace(ipName)); Contract.Requires(!string.IsNullOrWhiteSpace(location)); NetworkReservedIPGetResponse service = null; FlexStreams.Publish(new CheckIfExistsEvent(AzureResource.ReservedIp, ipName)); try { service = await client.ReservedIPs.GetAsync(ipName); } catch (CloudException cex) { if (cex.Error.Code != "ResourceNotFound") { throw; } } if (service != null) { FlexStreams.Publish(new FoundExistingEvent(AzureResource.ReservedIp, ipName)); return; } await client.ReservedIPs.CreateAsync( new NetworkReservedIPCreateParameters { Name = ipName, Location = location }); FlexStreams.Publish(new ProvisionEvent(AzureResource.ReservedIp, ipName)); }
/// <summary> /// Checks for the existence of a specific Azure Sql Database, if it doesn't exist it will create it. /// </summary> /// <param name="client">The <see cref="SqlManagementClient"/> that is performing the operation.</param> /// <param name="model">The DevOpsFlex rich model object that contains everything there is to know about this database spec.</param> /// <returns>The async <see cref="Task"/> wrapper.</returns> public static async Task CreateDatabaseIfNotExistsAsync(this SqlManagementClient client, SqlAzureDb model) { Contract.Requires(client != null); Contract.Requires(model != null); string serverName; lock (SqlServerGate) { FlexStreams.Publish(new CheckForParentResourceEvent(AzureResource.SqlServer, AzureResource.SqlDatabase, model.Name)); serverName = FlexConfiguration.SqlServerChooser.Choose(client, model.System.Location.GetEnumDescription()).Result; if (serverName == null) { string serverMaxVersion = null; try { client.Servers.Create( new ServerCreateParameters { AdministratorUserName = FlexConfiguration.FlexSaUser, AdministratorPassword = FlexConfiguration.FlexSaPwd, Location = model.System.Location.GetEnumDescription(), Version = "100.0" // This needs to be an invalid version number // TODO: START USING DATABASE SIZE. }); } catch (CloudException ex) { if (ex.Error.Code == "40856") // SQL Version doesn't exist in the target location { var serverVersions = Regex.Match(ex.Error.Message, "server versions: '([^']*)'.").Groups[1].Value.Split(','); var maxVersion = serverVersions.Max((s => decimal.Parse(s))); serverMaxVersion = serverVersions.First(s => s.Contains(maxVersion.ToString("F1", CultureInfo.InvariantCulture))); } else { throw; } } serverName = client.Servers.Create( new ServerCreateParameters { AdministratorUserName = FlexConfiguration.FlexSaUser, AdministratorPassword = FlexConfiguration.FlexSaPwd, Location = model.System.Location.GetEnumDescription(), Version = serverMaxVersion }).ServerName; FlexStreams.Publish(new ProvisionEvent(AzureResource.SqlServer, serverName)); } else { FlexStreams.Publish(new FoundExistingEvent(AzureResource.SqlServer, serverName)); } } var dbName = FlexConfiguration.GetNaming <SqlAzureDb>() .GetSlotName( model, FlexDataConfiguration.Branch, FlexDataConfiguration.Configuration); await client.CreateDatabaseIfNotExistsAsync(serverName, dbName, model.Edition.GetEnumDescription(), model.CollationName, model.MaximumDatabaseSizeInGB, model.CreateAppUser); }