public void GetOctopusTags_FindsMatchingTags_RegardlessOfCase() { // Arrange var tags = new Dictionary <string, string> { { "oCtoPus-eNviRonMenT", "taggedEnvironment" }, { "ocTopUs-roLe", "taggedRole" }, { "OctOpuS-ProJecT", "taggedProject" }, { "oCtoPus-sPacE", "taggedSpace" }, { "ocTopUs-teNanT", "taggedTenant" }, }; // Act var foundTags = AzureWebAppHelper.GetOctopusTags(tags); // Assert using (new AssertionScope()) { foundTags.Environment.Should().Be("taggedEnvironment"); foundTags.Role.Should().Be("taggedRole"); foundTags.Project.Should().Be("taggedProject"); foundTags.Space.Should().Be("taggedSpace"); foundTags.Tenant.Should().Be("taggedTenant"); } }
public void Install(RunningDeployment deployment) { var variables = deployment.Variables; var subscriptionId = variables.Get(SpecialVariables.Action.Azure.SubscriptionId); var resourceGroupName = variables.Get(SpecialVariables.Action.Azure.ResourceGroupName, string.Empty); var siteAndSlotName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndSlotName, slotName); var resourceGroupText = string.IsNullOrEmpty(resourceGroupName) ? string.Empty : $" in Resource Group '{resourceGroupName}'"; var slotText = targetSite.HasSlot ? $", deployment slot '{targetSite.Slot}'" : string.Empty; Log.Info($"Deploying to Azure WebApp '{targetSite.Site}'{slotText}{resourceGroupText}, using subscription-id '{subscriptionId}'"); var publishProfile = GetPublishProfile(variables); RemoteCertificateValidationCallback originalServerCertificateValidationCallback = null; try { originalServerCertificateValidationCallback = ServicePointManager.ServerCertificateValidationCallback; ServicePointManager.ServerCertificateValidationCallback = WrapperForServerCertificateValidationCallback; DeployToAzure(deployment, targetSite, variables, publishProfile); } finally { ServicePointManager.ServerCertificateValidationCallback = originalServerCertificateValidationCallback; } }
public void PoorlyFormedLegacySite() { var targetSite = AzureWebAppHelper.GetAzureTargetSite($"{site}({slot}", string.Empty); targetSite.HasSlot.Should().BeTrue(); targetSite.Site.Should().Be(site); targetSite.Slot.Should().Be(slot); }
public async Task Execute(RunningDeployment context) { // Read/Validate variables Log.Verbose("Starting App Settings Deploy"); var variables = context.Variables; var principalAccount = ServicePrincipalAccount.CreateFromKnownVariables(variables); var webAppName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); if (webAppName == null) { throw new Exception("Web App Name must be specified"); } var resourceGroupName = variables.Get(SpecialVariables.Action.Azure.ResourceGroupName); if (resourceGroupName == null) { throw new Exception("resource group name must be specified"); } var targetSite = AzureWebAppHelper.GetAzureTargetSite(webAppName, slotName, resourceGroupName); string token = await Auth.GetAuthTokenAsync(principalAccount); var webAppClient = new WebSiteManagementClient(new Uri(principalAccount.ResourceManagementEndpointBaseUri), new TokenCredentials(token)) { SubscriptionId = principalAccount.SubscriptionNumber, HttpClient = { BaseAddress = new Uri(principalAccount.ResourceManagementEndpointBaseUri) } }; // If app settings are specified if (variables.GetNames().Contains(SpecialVariables.Action.Azure.AppSettings) && !string.IsNullOrWhiteSpace(variables[SpecialVariables.Action.Azure.AppSettings])) { var appSettingsJson = variables.Get(SpecialVariables.Action.Azure.AppSettings, ""); Log.Verbose($"Updating application settings:\n{appSettingsJson}"); var appSettings = JsonConvert.DeserializeObject <AppSetting[]>(appSettingsJson); await PublishAppSettings(webAppClient, targetSite, appSettings, token); Log.Info("Updated application settings"); } // If connection strings are specified if (variables.GetNames().Contains(SpecialVariables.Action.Azure.ConnectionStrings) && !string.IsNullOrWhiteSpace(variables[SpecialVariables.Action.Azure.ConnectionStrings])) { var connectionStringsJson = variables.Get(SpecialVariables.Action.Azure.ConnectionStrings, ""); Log.Verbose($"Updating connection strings:\n{connectionStringsJson}"); var connectionStrings = JsonConvert.DeserializeObject <ConnectionStringSetting[]>(connectionStringsJson); await PublishConnectionStrings(webAppClient, targetSite, connectionStrings); Log.Info("Updated connection strings"); } }
public void SiteWithNoSlot() { var targetSite = AzureWebAppHelper.GetAzureTargetSite($"{site}", string.Empty); targetSite.HasSlot.Should().BeFalse(); targetSite.Site.Should().Be(site); targetSite.RawSite.Should().Be(site); targetSite.Slot.Should().BeNullOrEmpty(); }
public void SiteAndSlotInProperty() { var targetSite = AzureWebAppHelper.GetAzureTargetSite(site, slot); targetSite.HasSlot.Should().BeTrue(); targetSite.Site.Should().Be(site); targetSite.RawSite.Should().Be(site); targetSite.Slot.Should().Be(slot); targetSite.SiteAndSlot.Should().Be($"{site}/{slot}"); }
public void SiteWithSlotSlashFormat() { var siteAndMaybeSlotName = $"{site}/{slot}"; var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndMaybeSlotName, string.Empty); targetSite.HasSlot.Should().BeTrue(); targetSite.Site.Should().Be(site); targetSite.RawSite.Should().Be(siteAndMaybeSlotName); targetSite.Slot.Should().Be(slot); }
public void LegacySiteWithSlot() { var siteAndMaybeSlotName = $"{site}({slot})"; var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndMaybeSlotName, string.Empty); targetSite.HasSlot.Should().BeTrue(); targetSite.Site.Should().Be(this.site); targetSite.RawSite.Should().Be(siteAndMaybeSlotName); targetSite.Slot.Should().Be(this.slot); }
Task <WebDeployPublishSettings> GetPublishProfile(IVariables variables) { var account = new AzureServicePrincipalAccount(variables); var siteAndSlotName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndSlotName, slotName); return(resourceManagerPublishProfileProvider.GetPublishProperties(account, variables.Get(SpecialVariables.Action.Azure.ResourceGroupName, string.Empty), targetSite)); }
public async Task AzureLinuxContainerDeploy() { newVariables = new CalamariVariables(); AddVariables(newVariables); var runningContext = new RunningDeployment("", newVariables); await new AzureAppServiceDeployContainerBehavior(new InMemoryLog()).Execute(runningContext); await AssertDeploySuccessAsync(AzureWebAppHelper.GetAzureTargetSite(site.Name, "", resourceGroupName)); }
public void SiteWithSlotInSiteNameAndSlotInProperty() { var overrideSlot = Guid.NewGuid().ToString(); var siteAndMaybeSlotName = $"{site}/{slot}"; var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndMaybeSlotName, overrideSlot); targetSite.HasSlot.Should().BeTrue(); targetSite.Site.Should().Be(site); targetSite.RawSite.Should().Be(siteAndMaybeSlotName); targetSite.Slot.Should().Be(slot); }
public async Task AzureLinuxContainerSlotDeploy() { var slotName = "stage"; newVariables = new CalamariVariables(); AddVariables(newVariables); newVariables.Add("Octopus.Action.Azure.DeploymentSlot", slotName); var slot = await webMgmtClient.WebApps.BeginCreateOrUpdateSlotAsync(resourceGroupName, webappName, site, slotName); var runningContext = new RunningDeployment("", newVariables); await new AzureAppServiceDeployContainerBehavior(new InMemoryLog()).Execute(runningContext); await AssertDeploySuccessAsync(AzureWebAppHelper.GetAzureTargetSite(site.Name, slotName, resourceGroupName)); }
private static WebDeployPublishSettings GetPublishProfile(IVariables variables) { var account = new AzureServicePrincipalAccount(variables); var siteAndSlotName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndSlotName, slotName); if (account is AzureServicePrincipalAccount servicePrincipalAccount) { return(ResourceManagerPublishProfileProvider.GetPublishProperties(servicePrincipalAccount, variables.Get(SpecialVariables.Action.Azure.ResourceGroupName, string.Empty), targetSite)); } throw new CommandException("Account type must be Azure Service Principal"); }
public async Task Execute(RunningDeployment context) { var variables = context.Variables; var principalAccount = ServicePrincipalAccount.CreateFromKnownVariables(variables); var webAppName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var rgName = variables.Get(SpecialVariables.Action.Azure.ResourceGroupName); var targetSite = AzureWebAppHelper.GetAzureTargetSite(webAppName, slotName, rgName); var image = variables.Get(SpecialVariables.Action.Package.Image); var registryHost = variables.Get(SpecialVariables.Action.Package.Registry); var regUsername = variables.Get(SpecialVariables.Action.Package.Feed.Username); var regPwd = variables.Get(SpecialVariables.Action.Package.Feed.Password); var token = await Auth.GetAuthTokenAsync(principalAccount); var webAppClient = new WebSiteManagementClient(new Uri(principalAccount.ResourceManagementEndpointBaseUri), new TokenCredentials(token)) { SubscriptionId = principalAccount.SubscriptionNumber }; Log.Info($"Updating web app to use image {image} from registry {registryHost}"); Log.Verbose("Retrieving config (this is required to update image)"); var config = await webAppClient.WebApps.GetConfigurationAsync(targetSite); config.LinuxFxVersion = $@"DOCKER|{image}"; Log.Verbose("Retrieving app settings"); var appSettings = await webAppClient.WebApps.ListApplicationSettingsAsync(targetSite); appSettings.Properties["DOCKER_REGISTRY_SERVER_URL"] = "https://" + registryHost; appSettings.Properties["DOCKER_REGISTRY_SERVER_USERNAME"] = regUsername; appSettings.Properties["DOCKER_REGISTRY_SERVER_PASSWORD"] = regPwd; Log.Info("Updating app settings with container registry"); await webAppClient.WebApps.UpdateApplicationSettingsAsync(targetSite, appSettings); Log.Info("Updating configuration with container image"); await webAppClient.WebApps.UpdateConfigurationAsync(targetSite, config); }
private static SitePublishProfile GetPublishProfile(VariableDictionary variables) { var subscriptionId = variables.Get(SpecialVariables.Action.Azure.SubscriptionId); var accountType = variables.Get(SpecialVariables.Account.AccountType); var siteAndSlotName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndSlotName, slotName); switch (accountType) { case AzureAccountTypes.ServicePrincipalAccountType: var resourceManagementEndpoint = variables.Get(SpecialVariables.Action.Azure.ResourceManagementEndPoint, DefaultVariables.ResourceManagementEndpoint); if (resourceManagementEndpoint != DefaultVariables.ResourceManagementEndpoint) { Log.Info("Using override for resource management endpoint - {0}", resourceManagementEndpoint); } var activeDirectoryEndpoint = variables.Get(SpecialVariables.Action.Azure.ActiveDirectoryEndPoint, DefaultVariables.ActiveDirectoryEndpoint); if (activeDirectoryEndpoint != DefaultVariables.ActiveDirectoryEndpoint) { Log.Info("Using override for Azure Active Directory endpoint - {0}", activeDirectoryEndpoint); } return(ResourceManagerPublishProfileProvider.GetPublishProperties(subscriptionId, variables.Get(SpecialVariables.Action.Azure.ResourceGroupName, string.Empty), targetSite, variables.Get(SpecialVariables.Action.Azure.TenantId), variables.Get(SpecialVariables.Action.Azure.ClientId), variables.Get(SpecialVariables.Action.Azure.Password), resourceManagementEndpoint, activeDirectoryEndpoint)); case AzureAccountTypes.ManagementCertificateAccountType: var serviceManagementEndpoint = variables.Get(SpecialVariables.Action.Azure.ServiceManagementEndPoint, DefaultVariables.ServiceManagementEndpoint); if (serviceManagementEndpoint != DefaultVariables.ServiceManagementEndpoint) { Log.Info("Using override for service management endpoint - {0}", serviceManagementEndpoint); } return(ServiceManagementPublishProfileProvider.GetPublishProperties(subscriptionId, Convert.FromBase64String(variables.Get(SpecialVariables.Action.Azure.CertificateBytes)), targetSite, serviceManagementEndpoint)); default: throw new CommandException( "Account type must be either Azure Management Certificate or Azure Service Principal"); } }
public async Task Execute(RunningDeployment context) { var variables = context.Variables; var webAppName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var resourceGroupName = variables.Get(SpecialVariables.Action.Azure.ResourceGroupName); var targetSite = AzureWebAppHelper.GetAzureTargetSite(webAppName, slotName, resourceGroupName); var principalAccount = ServicePrincipalAccount.CreateFromKnownVariables(variables); var token = await Auth.GetAuthTokenAsync(principalAccount); var webAppClient = new WebSiteManagementClient(new Uri(principalAccount.ResourceManagementEndpointBaseUri), new TokenCredentials(token)) { SubscriptionId = principalAccount.SubscriptionNumber }; Log.Info("Performing soft restart of web app"); await webAppClient.WebApps.RestartAsync(targetSite, true); }
public void Install(RunningDeployment deployment) { try { var variables = deployment.Variables; var resourceGroupName = variables.Get(SpecialVariables.Action.Azure.ResourceGroupName, string.Empty); var siteAndSlotName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndSlotName, slotName); var azureEnvironment = variables.Get(SpecialVariables.Action.Azure.Environment); var account = AccountFactory.Create(variables); if (account is AzureServicePrincipalAccount servicePrincipalAccount) { var client = servicePrincipalAccount.CreateWebSiteManagementClient(); var site = client?.WebApps.Get(resourceGroupName, siteAndSlotName); if (site != null) { var portalUrl = GetAzurePortalUrl(azureEnvironment); Log.Info($"Default Host Name: {site.DefaultHostName}"); Log.Info($"Application state: {site.State}"); Log.Info("Links:"); LogLink($"https://{site.DefaultHostName}"); if (!site.HttpsOnly.HasValue || site.HttpsOnly == false) { LogLink($"http://{site.DefaultHostName}"); } string portalUri = $"https://{portalUrl}/#@/resource{site.Id}"; LogLink("View in Azure Portal", portalUri); } } } catch { // do nothing } }
private static WebDeployPublishSettings GetPublishProfile(VariableDictionary variables) { var account = AccountFactory.Create(variables); var siteAndSlotName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndSlotName, slotName); if (account is AzureServicePrincipalAccount servicePrincipalAccount) { return(ResourceManagerPublishProfileProvider.GetPublishProperties(servicePrincipalAccount, variables.Get(SpecialVariables.Action.Azure.ResourceGroupName, string.Empty), targetSite)); } else if (account is AzureAccount azureAccount) { return(new WebDeployPublishSettings(targetSite.Site, ServiceManagementPublishProfileProvider.GetPublishProperties(azureAccount, targetSite))); } throw new CommandException("Account type must be either Azure Management Certificate or Azure Service Principal"); }
public void Install(RunningDeployment deployment) { var variables = deployment.Variables; if (variables.Get(SpecialVariables.Account.AccountType) == "AzureSubscription") { log.Warn("Use of Management Certificates to deploy Azure Web App services has been deprecated by Microsoft and they are progressively disabling them, please update to using a Service Principal. If you receive an error about the app not being found in the subscription then this account type is the most likely cause. See [our documentation](https://g.octopushq.com/AzureTargets#azure-management-certificate) for more details."); } var subscriptionId = variables.Get(SpecialVariables.Action.Azure.SubscriptionId); var resourceGroupName = variables.Get(SpecialVariables.Action.Azure.ResourceGroupName, string.Empty); var siteAndSlotName = variables.Get(SpecialVariables.Action.Azure.WebAppName); var slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var targetSite = AzureWebAppHelper.GetAzureTargetSite(siteAndSlotName, slotName); var resourceGroupText = string.IsNullOrEmpty(resourceGroupName) ? string.Empty : $" in Resource Group '{resourceGroupName}'"; var slotText = targetSite.HasSlot ? $", deployment slot '{targetSite.Slot}'" : string.Empty; log.Info($"Deploying to Azure WebApp '{targetSite.Site}'{slotText}{resourceGroupText}, using subscription-id '{subscriptionId}'"); var publishSettings = GetPublishProfile(variables); RemoteCertificateValidationCallback originalServerCertificateValidationCallback = null; try { originalServerCertificateValidationCallback = ServicePointManager.ServerCertificateValidationCallback; ServicePointManager.ServerCertificateValidationCallback = WrapperForServerCertificateValidationCallback; DeployToAzure(deployment, targetSite, variables, publishSettings); } finally { ServicePointManager.ServerCertificateValidationCallback = originalServerCertificateValidationCallback; } }
public async Task Execute(RunningDeployment runningDeployment) { await Task.CompletedTask; var targetDiscoveryContext = GetTargetDiscoveryContext(runningDeployment.Variables); if (targetDiscoveryContext?.Authentication == null || targetDiscoveryContext.Scope == null) { Log.Warn("Aborting target discovery."); return; } var account = targetDiscoveryContext.Authentication.AccountDetails; Log.Verbose($"Looking for Azure web apps using:"); Log.Verbose($" Subscription ID: {account.SubscriptionNumber}"); Log.Verbose($" Tenant ID: {account.TenantId}"); Log.Verbose($" Client ID: {account.ClientId}"); var azureClient = account.CreateAzureClient(); try { var discoveredTargetCount = 0; var webApps = ListWebApps(azureClient); Log.Verbose($"Found {webApps.Count()} candidate web apps."); foreach (var webApp in webApps) { var tags = AzureWebAppHelper.GetOctopusTags(webApp.Tags); var matchResult = targetDiscoveryContext.Scope.Match(tags); if (matchResult.IsSuccess) { discoveredTargetCount++; Log.Info($"Discovered matching web app: {webApp.Name}"); WriteTargetCreationServiceMessage( webApp, targetDiscoveryContext, matchResult); } else { Log.Verbose($"Web app {webApp.Name} does not match target requirements:"); foreach (var reason in matchResult.FailureReasons) { Log.Verbose($"- {reason}"); } } Log.Verbose($"Looking for deployment slots in web app {webApp.Name}"); var deploymentSlots = ListDeploymentSlots(webApp); foreach (var slot in deploymentSlots) { var deploymentSlotTags = AzureWebAppHelper.GetOctopusTags(slot.Tags); var deploymentSlotMatchResult = targetDiscoveryContext.Scope.Match(deploymentSlotTags); if (deploymentSlotMatchResult.IsSuccess) { discoveredTargetCount++; Log.Info($"Discovered matching deployment slot {slot.Name} in web app {webApp.Name}"); WriteDeploymentSlotTargetCreationServiceMessage( webApp, slot, targetDiscoveryContext, deploymentSlotMatchResult); } else { Log.Verbose($"Deployment slot {slot.Name} in web app {webApp.Name} does not match target requirements:"); foreach (var reason in matchResult.FailureReasons) { Log.Verbose($"- {reason}"); } } } } if (discoveredTargetCount > 0) { Log.Info($"{discoveredTargetCount} targets found."); } else { Log.Warn($"Could not find any Azure web app targets."); } } catch (Exception ex) { Log.Warn($"Error connecting to Azure to look for web apps:"); Log.Warn(ex.Message); Log.Warn("Aborting target discovery."); } }
public async Task Execute(RunningDeployment context) { var variables = context.Variables; var servicePrincipal = ServicePrincipalAccount.CreateFromKnownVariables(variables); string?webAppName = variables.Get(SpecialVariables.Action.Azure.WebAppName); if (webAppName == null) { throw new Exception("Web App Name must be specified"); } string?resourceGroupName = variables.Get(SpecialVariables.Action.Azure.ResourceGroupName); if (resourceGroupName == null) { throw new Exception("resource group name must be specified"); } string?slotName = variables.Get(SpecialVariables.Action.Azure.WebAppSlot); var packageFileInfo = new FileInfo(variables.Get(TentacleVariables.CurrentDeployment.PackageFilePath) !); switch (packageFileInfo.Extension) { case ".zip": Archive = new ZipPackageProvider(); break; case ".nupkg": Archive = new NugetPackageProvider(); break; case ".war": Archive = new WarPackageProvider(Log, variables, context); break; default: throw new Exception("Unsupported archive type"); } var azureClient = servicePrincipal.CreateAzureClient(); var webApp = await azureClient.WebApps.GetByResourceGroupAsync(resourceGroupName, webAppName); var targetSite = AzureWebAppHelper.GetAzureTargetSite(webAppName, slotName, resourceGroupName); // Lets process our archive while the slot is spun up. we will await it later before we try to upload to it. var slotCreateTask = new Task(() => { }); if (targetSite.HasSlot) { slotCreateTask = FindOrCreateSlot(webApp, targetSite); } string[]? substitutionFeatures = { KnownVariables.Features.ConfigurationTransforms, KnownVariables.Features.StructuredConfigurationVariables, KnownVariables.Features.SubstituteInFiles }; /* * Calamari default behaviors * https://github.com/OctopusDeploy/Calamari/tree/master/source/Calamari.Common/Features/Behaviours */ var uploadPath = string.Empty; if (substitutionFeatures.Any(featureName => context.Variables.IsFeatureEnabled(featureName))) { uploadPath = (await Archive.PackageArchive(context.StagingDirectory, context.CurrentDirectory)) .FullName; } else { uploadPath = (await Archive.ConvertToAzureSupportedFile(packageFileInfo)).FullName; } if (uploadPath == null) { throw new Exception("Package File Path must be specified"); } // need to ensure slot is created as slot creds may be used if (targetSite.HasSlot) { await slotCreateTask; } var publishingProfile = await PublishingProfile.GetPublishingProfile(targetSite, servicePrincipal); string?credential = await Auth.GetBasicAuthCreds(servicePrincipal, targetSite); string token = await Auth.GetAuthTokenAsync(servicePrincipal); var webAppClient = new WebSiteManagementClient(new Uri(servicePrincipal.ResourceManagementEndpointBaseUri), new TokenCredentials(token)) { SubscriptionId = servicePrincipal.SubscriptionNumber }; var httpClient = webAppClient.HttpClient; httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credential); Log.Info($"Uploading package to {targetSite.SiteAndSlot}"); await UploadZipAsync(publishingProfile, httpClient, uploadPath, targetSite.ScmSiteAndSlot); }