internal async Task <bool> AddAsync(InstanceCreateNames instance, string location, string requiredVersion, string sourceUrl, InstanceFineTuning tuning, CancellationToken cancellationToken) { string rgName = instance.ResourceGroupName; bool ok = await MakeSureResourceGroupExistsAsync(instance.IsCustom, location, rgName, cancellationToken); if (!ok) { return(false); } ok = await DeployArmTemplateAsync(instance, location, rgName, tuning, cancellationToken); if (!ok) { return(false); } // check runtime package var package = new FunctionRuntimePackage(logger); ok = await package.UpdateVersionAsync(requiredVersion, sourceUrl, instance, azure, cancellationToken); if (ok) { var devopsLogonData = DevOpsLogon.Load().connection; if (devopsLogonData.Mode == DevOpsTokenType.PAT) { logger.WriteVerbose($"Saving Azure DevOps token"); ok = await ChangeAppSettingsAsync(instance, devopsLogonData, SaveMode.Default, cancellationToken); if (ok) { logger.WriteInfo($"Azure DevOps token saved"); } else { logger.WriteError($"Failed to save Azure DevOps token"); } } else { logger.WriteWarning($"Azure DevOps token type {devopsLogonData.Mode} is unsupported"); ok = false; } } return(ok); }
private async Task <bool> DeployArmTemplateAsync(InstanceCreateNames instance, string location, string rgName, InstanceFineTuning tuning, CancellationToken cancellationToken) { // IDEA the template should create a Storage account and/or a Key Vault for Rules' use // TODO https://github.com/gjlumsden/AzureFunctionsSlots suggest that slots must be created in template var resourceName = "aggregator.cli.Instances.instance-template.json"; string armTemplateString; var assembly = Assembly.GetExecutingAssembly(); using (Stream stream = assembly.GetManifestResourceStream(resourceName)) using (StreamReader reader = new StreamReader(stream)) { armTemplateString = await reader.ReadToEndAsync(); } var parsedTemplate = JObject.Parse(armTemplateString); // sanity checks if (parsedTemplate.SelectToken("parameters.aggregatorVersion") == null) { // not good, blah logger.WriteWarning($"Something is wrong with the ARM template"); return(false); } var infoVersion = GetCustomAttribute <AssemblyInformationalVersionAttribute>(); var templateParams = new Dictionary <string, Dictionary <string, object> > { // TODO give use more control by setting more parameters { "webLocation", new Dictionary <string, object> { { "value", location } } }, { "aiLocation", new Dictionary <string, object> { { "value", tuning.AppInsightLocation } } }, { "storageAccountType", new Dictionary <string, object> { { "value", "Standard_LRS" } } }, { "functionAppName", new Dictionary <string, object> { { "value", instance.FunctionAppName } } }, { "storageAccountName", new Dictionary <string, object> { { "value", instance.StorageAccountName } } }, { "hostingPlanName", new Dictionary <string, object> { { "value", instance.HostingPlanName } } }, { "appInsightName", new Dictionary <string, object> { { "value", instance.AppInsightName } } }, { "aggregatorVersion", new Dictionary <string, object> { { "value", infoVersion.InformationalVersion } } }, { "hostingPlanSkuName", new Dictionary <string, object> { { "value", tuning.HostingPlanSku } } }, { "hostingPlanSkuTier", new Dictionary <string, object> { { "value", tuning.HostingPlanTier } } }, }; string deploymentName = SdkContext.RandomResourceName("aggregator", 24); logger.WriteInfo($"Started deployment (id: {deploymentName})"); var deployment = await azure.Deployments.Define(deploymentName) .WithExistingResourceGroup(rgName) .WithTemplate(armTemplateString) .WithParameters(templateParams) .WithMode(DeploymentMode.Incremental) .CreateAsync(cancellationToken); // poll const int pollIntervalInSeconds = 3; int totalDelay = 0; while (!(StringComparer.OrdinalIgnoreCase.Equals(deployment.ProvisioningState, "Succeeded") || StringComparer.OrdinalIgnoreCase.Equals(deployment.ProvisioningState, "Failed") || StringComparer.OrdinalIgnoreCase.Equals(deployment.ProvisioningState, "Cancelled"))) { SdkContext.DelayProvider.Delay(pollIntervalInSeconds * 1000); totalDelay += pollIntervalInSeconds; logger.WriteVerbose($"Deployment running ({totalDelay}s)"); await deployment.RefreshAsync(cancellationToken); } logger.WriteInfo($"Deployment {deployment.ProvisioningState}"); return(deployment.ProvisioningState == "Succeeded"); }