public AppStack() { var config = new Config(); var resourceGroup = new ResourceGroup("rotatesecretoneset-rg"); var sqlAdminLogin = config.Get("sqlAdminLogin") ?? "sqlAdmin"; var sqlAdminPassword = new RandomUuid("sqlPassword").Result; var sqlServer = new Server("sqlServer", new ServerArgs { AdministratorLogin = sqlAdminLogin, AdministratorLoginPassword = sqlAdminPassword, ResourceGroupName = resourceGroup.Name, Version = "12.0", }); new FirewallRule("AllowAllWindowsAzureIps", new FirewallRuleArgs { ServerName = sqlServer.Name, ResourceGroupName = resourceGroup.Name, StartIpAddress = "0.0.0.0", EndIpAddress = "0.0.0.0", }); var clientConfig = Output.Create(GetClientConfig.InvokeAsync()); var tenantId = clientConfig.Apply(c => c.TenantId); var storageAccount = new StorageAccount("storageaccount", new StorageAccountArgs { Kind = "Storage", ResourceGroupName = resourceGroup.Name, Sku = new Storage.Inputs.SkuArgs { Name = "Standard_LRS" }, }); var appInsights = new Component("appInsights", new ComponentArgs { RequestSource = "IbizaWebAppExtensionCreate", ResourceGroupName = resourceGroup.Name, ApplicationType = "web", Kind = "web", Tags = { //{ "[concat('hidden-link:', resourceId('Microsoft.Web/sites', parameters('functionAppName')))]", "Resource" }, }, }); var secretName = config.Get("secretName") ?? "sqlPassword"; var appService = new AppServicePlan("functionApp-appService", new AppServicePlanArgs { ResourceGroupName = resourceGroup.Name, Sku = new SkuDescriptionArgs { Name = "Y1", Tier = "Dynamic", }, }); var storageKey = Output.Tuple(resourceGroup.Name, storageAccount.Name).Apply(v => { var task = ListStorageAccountKeys.InvokeAsync(new ListStorageAccountKeysArgs { AccountName = v.Item2, ResourceGroupName = v.Item1 }); return(Output.Create(task).Apply(t => t.Keys[0].Value)); }); var functionApp = new WebApp("functionApp", new WebAppArgs { Kind = "functionapp", ResourceGroupName = resourceGroup.Name, ServerFarmId = appService.Id, Identity = new ManagedServiceIdentityArgs { Type = ManagedServiceIdentityType.SystemAssigned }, SiteConfig = new SiteConfigArgs { AppSettings = { new NameValuePairArgs { Name = "AzureWebJobsStorage", Value = Output.Format($"DefaultEndpointsProtocol=https;AccountName={storageAccount.Name};AccountKey={storageKey}"), }, new NameValuePairArgs { Name = "FUNCTIONS_EXTENSION_VERSION", Value = "~3", }, new NameValuePairArgs { Name = "FUNCTIONS_WORKER_RUNTIME", Value = "dotnet", }, new NameValuePairArgs { Name = "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", Value = Output.Format($"DefaultEndpointsProtocol=https;AccountName={storageAccount.Name};EndpointSuffix=core.windows.net;AccountKey={storageKey}"), }, new NameValuePairArgs { Name = "WEBSITE_NODE_DEFAULT_VERSION", Value = "~10", }, new NameValuePairArgs { Name = "APPINSIGHTS_INSTRUMENTATIONKEY", Value = appInsights.InstrumentationKey, }, }, }, }); var functionAppSourceControl = new WebAppSourceControl("functionApp-sourceControl", new WebAppSourceControlArgs { Name = functionApp.Name, IsManualIntegration = true, Branch = "main", RepoUrl = config.Get("functionAppRepoURL") ?? "https://github.com/Azure-Samples/KeyVault-Rotation-SQLPassword-Csharp.git", ResourceGroupName = resourceGroup.Name, }); var webAppAppService = new AppServicePlan("webApp-appService", new AppServicePlanArgs { ResourceGroupName = resourceGroup.Name, Sku = new SkuDescriptionArgs { Name = "F1", }, }); var webApp = new WebApp("webApp", new WebAppArgs { Kind = "app", ResourceGroupName = resourceGroup.Name, ServerFarmId = webAppAppService.Id, Identity = new ManagedServiceIdentityArgs { Type = ManagedServiceIdentityType.SystemAssigned }, }); var keyVault = new Vault("keyVault", new VaultArgs { Properties = new VaultPropertiesArgs { AccessPolicies = { new AccessPolicyEntryArgs { TenantId = tenantId, ObjectId = functionApp.Identity.Apply(i => i !.PrincipalId), Permissions = new PermissionsArgs { Secrets ={ "get", "list", "set" }, }, },
public TdStack() { string stackName = Pulumi.Deployment.Instance.StackName; Config pulumiConfig = new(); var sqlDatabaseDbAdminId = pulumiConfig.Require("sql-server-td-db-admin-id"); var sqlDatabaseDbAdminPassword = pulumiConfig.RequireSecret("sql-server-td-db-admin-password"); var sqlDatabaseDbUserId = pulumiConfig.Require("sql-server-td-db-user-id"); var sqlDatabaseDbUserPassword = pulumiConfig.RequireSecret("sql-server-td-db-user-password"); var defaultEmailFrom = pulumiConfig.Require("default-email-from"); var emailServerHost = pulumiConfig.Require("email-server-host"); var emailServerPort = pulumiConfig.Require("email-server-port"); var emailServerUserName = pulumiConfig.Require("email-server-userName"); var emailServerPassword = pulumiConfig.RequireSecret("email-server-password"); var jwtSecretKey = pulumiConfig.RequireSecret("jwt-secret-key"); var azureDevOpsAgentVMIPAddress = pulumiConfig.Require("azure-dev-ops-agent-vm-ip"); ResourceGroup resourceGroup = new($"td-{stackName}", new ResourceGroupArgs { ResourceGroupName = $"td-{stackName}" }, options : new() { ImportId = $"/subscriptions/{GetClientConfig.InvokeAsync().GetAwaiter().GetResult().SubscriptionId}/resourceGroups/td-test" }); Workspace appInsightsWorkspace = new($"insights-wkspc-td-{stackName}", new() { WorkspaceName = $"insights-wkspc-td-{stackName}", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, RetentionInDays = 30 }); AppInsights appInsights = new($"app-insights-td-{stackName}", new() { ResourceName = $"app-insights-td-{stackName}", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, ApplicationType = AppInsightsWebApplicationType.Web, Kind = "web", IngestionMode = AppInsightsIngestionMode.LogAnalytics, DisableIpMasking = true, WorkspaceResourceId = Output.Tuple(resourceGroup.Name, appInsightsWorkspace.Name).Apply(t => { (string resourceGroupName, string workspaceName) = t; return(GetWorkspace.InvokeAsync(new GetWorkspaceArgs { ResourceGroupName = resourceGroupName, WorkspaceName = workspaceName })); }).Apply(workspace => workspace.Id) }); SqlServer sqlServer = new($"sql-server-td-{stackName}", new() { ServerName = $"sql-server-td-{stackName}", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, AdministratorLogin = sqlDatabaseDbAdminId, AdministratorLoginPassword = sqlDatabaseDbAdminPassword }); SqlDatabase sqlDatabase = new($"sql-database-td-{stackName}", new() { DatabaseName = $"sql-database-td-{stackName}", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, ServerName = sqlServer.Name, Sku = new SqlDatabaseSkuArgs { Tier = "Basic", Name = "Basic", Capacity = 5 } }); AppServicePlan appServicePlan = new($"app-plan-td-{stackName}", new() { Name = $"app-plan-td-{stackName}", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, Kind = "Linux", Reserved = true, Sku = new SkuDescriptionArgs { Tier = "Basic", Name = "B1", Size = "B1", Capacity = 1, Family = "B" } }); string vaultName = $"vault-td-{stackName}"; string sqlDatabaseConnectionStringSecretName = $"sql-connection-secret"; string blobStorageConnectionStringSecretName = $"blob-connection-secret"; string emailServerPasswordSecretName = "email-server-password-secret"; string jwtSecretKeySecretName = "jwt-secret-key-secret"; WebApp webApp = new($"app-service-td-{stackName}", new() { Name = $"app-service-td-{stackName}", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, ServerFarmId = appServicePlan.Id, ClientAffinityEnabled = false, HttpsOnly = true, Identity = new WebAppManagedServiceIdentityArgs() { Type = ManagedServiceIdentityType.SystemAssigned }, SiteConfig = new SiteConfigArgs { Use32BitWorkerProcess = false, AlwaysOn = true, Http20Enabled = true, WebSocketsEnabled = true, NetFrameworkVersion = "v6.0", FtpsState = FtpsState.Disabled, LinuxFxVersion = "DOTNETCORE|6.0", AppCommandLine = "dotnet TodoTemplate.Api.dll", AppSettings = new() { new NameValuePairArgs { Name = "ApplicationInsights__InstrumentationKey", Value = appInsights.InstrumentationKey }, new NameValuePairArgs { Name = "APPINSIGHTS_INSTRUMENTATIONKEY", Value = appInsights.InstrumentationKey }, new NameValuePairArgs { Name = "ASPNETCORE_ENVIRONMENT", Value = stackName == "test" ? "Test" : "Production" }, new NameValuePairArgs { Name = "APPLICATIONINSIGHTS_CONNECTION_STRING", Value = appInsights.ConnectionString }, new NameValuePairArgs { Name = "APPINSIGHTS_PROFILERFEATURE_VERSION", Value = "disabled" }, new NameValuePairArgs { Name = "APPINSIGHTS_SNAPSHOTFEATURE_VERSION", Value = "disabled" }, new NameValuePairArgs { Name = "ApplicationInsightsAgent_EXTENSION_VERSION", Value = "~3" }, new NameValuePairArgs { Name = "XDT_MicrosoftApplicationInsights_BaseExtensions", Value = "~1" }, new NameValuePairArgs { Name = "InstrumentationEngine_EXTENSION_VERSION", Value = "~1" }, new NameValuePairArgs { Name = "SnapshotDebugger_EXTENSION_VERSION", Value = "disabled" }, new NameValuePairArgs { Name = "XDT_MicrosoftApplicationInsights_Mode", Value = "recommended" }, new NameValuePairArgs { Name = "XDT_MicrosoftApplicationInsights_PreemptSdk", Value = "disabled" }, new NameValuePairArgs { Name = "AppSettings__EmailSettings__DefaulFromEmail", Value = defaultEmailFrom }, new NameValuePairArgs { Name = "AppSettings__EmailSettings__Host", Value = emailServerHost }, new NameValuePairArgs { Name = "AppSettings__EmailSettings__Port", Value = emailServerPort }, new NameValuePairArgs { Name = "AppSettings__EmailSettings__UserName", Value = emailServerUserName }, new NameValuePairArgs { Name = "AppSettings__EmailSettings__Password", Value = $"@Microsoft.KeyVault(VaultName={vaultName};SecretName={emailServerPasswordSecretName})" }, new NameValuePairArgs { Name = "AppSettings__JwtSettings__SecretKey", Value = $"@Microsoft.KeyVault(VaultName={vaultName};SecretName={jwtSecretKeySecretName})" }, }, ConnectionStrings = new() { new ConnStringInfoArgs { Name = "SqlServerConnectionString", Type = ConnectionStringType.SQLAzure, ConnectionString = $"@Microsoft.KeyVault(VaultName={vaultName};SecretName={sqlDatabaseConnectionStringSecretName})" }, new ConnStringInfoArgs { Name = "AzureBlobStorageConnectionString", Type = ConnectionStringType.Custom, ConnectionString = $"@Microsoft.KeyVault(VaultName={vaultName};SecretName={blobStorageConnectionStringSecretName})" } } } }); StorageAccount blobStorageAccount = new($"storageacctd{stackName}", new() { AccountName = $"storageacctd{stackName}", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, AccessTier = AccessTier.Hot, Kind = StorageKind.BlobStorage, Sku = new StorageAccountSkuArgs { Name = StorageSkuName.Standard_LRS } }); BlobContainer attachmentsContainer = new("attachments", new() { ResourceGroupName = resourceGroup.Name, AccountName = blobStorageAccount.Name, ContainerName = $"attachments", PublicAccess = PublicAccess.None }); new SqlServerFirewallRule($"fw-{stackName}-azure-dev-ops-agent-vm-ip", new() { Name = $"fw-{stackName}-azure-dev-ops-agent-vm-ip", FirewallRuleName = $"fw-{stackName}-azure-dev-ops-agent-vm-ip", EndIpAddress = azureDevOpsAgentVMIPAddress, ResourceGroupName = resourceGroup.Name, ServerName = sqlServer.Name, StartIpAddress = azureDevOpsAgentVMIPAddress }); Output.Tuple(resourceGroup.Name, webApp.Name).Apply(t => { (string resourceGroupName, string webAppName) = t; Output.Create(GetWebApp.InvokeAsync(new() { Name = webAppName, ResourceGroupName = resourceGroupName })) .Apply(webAppToGetIPAddresses => { var ipAddresses = webAppToGetIPAddresses.PossibleOutboundIpAddresses; foreach (var ipAddress in ipAddresses.Split(',')) { new SqlServerFirewallRule($"fw-{stackName}-{ipAddress}", new() { Name = $"fw-{stackName}-{ipAddress}", FirewallRuleName = $"fw-{stackName}-{ipAddress}", EndIpAddress = ipAddress, ResourceGroupName = resourceGroup.Name, ServerName = sqlServer.Name, StartIpAddress = ipAddress }); } return(string.Empty); }); return(string.Empty); }); Vault vault = new Vault($"vault-td-{stackName}", new() { ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, VaultName = vaultName, Properties = new VaultPropertiesArgs() { Sku = new VaultSkuArgs { Name = VaultSkuName.Standard, Family = SkuFamily.A }, TenantId = Output.Create(GetClientConfig.InvokeAsync()).Apply(clientConfig => clientConfig.TenantId), EnabledForDeployment = true, EnabledForDiskEncryption = true, EnabledForTemplateDeployment = true, EnableSoftDelete = false, AccessPolicies = new List <AccessPolicyEntryArgs> { new AccessPolicyEntryArgs { TenantId = Output.Create(GetClientConfig.InvokeAsync()).Apply(clientConfig => clientConfig.TenantId), ObjectId = Output.Tuple(resourceGroup.Name, webApp.Name).Apply(t => { (string resourceGroupName, string webAppName) = t; return(GetWebApp.InvokeAsync(new GetWebAppArgs { ResourceGroupName = resourceGroupName, Name = webAppName })); }).Apply(app => app.Identity !.PrincipalId), Permissions = new PermissionsArgs { Secrets = { "get" } } } } } }); Secret jwtSecretKeySecret = new(jwtSecretKeySecretName, new() { ResourceGroupName = resourceGroup.Name, VaultName = vault.Name, SecretName = jwtSecretKeySecretName, Properties = new SecretPropertiesArgs { Value = jwtSecretKey } }); Secret emailServerPasswordSecret = new(emailServerPasswordSecretName, new() { ResourceGroupName = resourceGroup.Name, VaultName = vault.Name, SecretName = emailServerPasswordSecretName, Properties = new SecretPropertiesArgs { Value = emailServerPassword } }); Secret sqlDatabaseConnectionStringSecret = new(sqlDatabaseConnectionStringSecretName, new() { ResourceGroupName = resourceGroup.Name, VaultName = vault.Name, SecretName = sqlDatabaseConnectionStringSecretName, Properties = new SecretPropertiesArgs { Value = Output.Tuple(sqlServer.Name, sqlDatabase.Name, sqlDatabaseDbUserPassword).Apply(t => { (string _sqlServer, string _sqlDatabase, string _sqlDatabasePassword) = t; return($"Data Source=tcp:{_sqlServer}.database.windows.net;Initial Catalog={_sqlDatabase};User ID={sqlDatabaseDbUserId};Password={_sqlDatabasePassword};Application Name=Todo;Encrypt=True;"); }) } }); var attachmentsContainerConnectionString = Output.Tuple(resourceGroup.Name, blobStorageAccount.Name, attachmentsContainer.Name) .Apply(t => { (string resourceGroupName, string blobStorageAccountName, string attachmentsContainerName) = t; var result = Output.Create(GetStorageAccountPrimaryKey(resourceGroupName, blobStorageAccountName)) .Apply(blobStorageAccountKey => { BlobSasBuilder blobSasBuilder = new BlobSasBuilder { BlobContainerName = attachmentsContainerName, Protocol = SasProtocol.Https, StartsOn = DateTimeOffset.Parse("2022-01-01T22:00:00Z"), ExpiresOn = DateTimeOffset.Parse("2032-01-01T22:00:00Z"), Resource = "c" }; blobSasBuilder.SetPermissions(BlobSasPermissions.Read | BlobSasPermissions.Create | BlobSasPermissions.Delete); var blobStorageConnectionString = $"https://{blobStorageAccountName}.blob.core.windows.net/{attachmentsContainerName}?{blobSasBuilder.ToSasQueryParameters(new StorageSharedKeyCredential(blobStorageAccountName, blobStorageAccountKey))}"; return(blobStorageConnectionString); }); return(result); }); Secret blobStorageConnectionStringSecret = new(blobStorageConnectionStringSecretName, new() { ResourceGroupName = resourceGroup.Name, VaultName = vault.Name, SecretName = blobStorageConnectionStringSecretName, Properties = new SecretPropertiesArgs { Value = attachmentsContainerConnectionString } }); }