public Infrastructure()
        {
            var currentConfig       = Output.Create(GetClientConfig.InvokeAsync());
            var currentUserObjectId = currentConfig.Apply(c => c.ObjectId);
            var tenantId            = currentConfig.Apply(c => c.TenantId);

            var resourceGroup = new ResourceGroupResource();

            var keyVault = new KeyVaultResource(resourceGroup, tenantId);

            var appServicePlan = new AppServicePlanResource(resourceGroup);

            appServicePlan.Build();

            var appService = new AppServiceResource(resourceGroup);

            // key vault needs to be build before any resource that accesses its secrets
            keyVault.Build();
            appService.AddConfiguration(keyVault, new[] { "KeyVaultName" });

            var staticWebsite = new StorageAccountResource(resourceGroup, "sw");

            staticWebsite.Build();
            staticWebsite.BuildStaticWebsite();

            var storageAccount = new StorageAccountResource(resourceGroup);

            storageAccount.Build();

            var applicationInsights = new ApplicationInsightsResource(resourceGroup);

            applicationInsights.Build();
            appService.AddConfiguration(applicationInsights, new[] { "APPINSIGHTS_INSTRUMENTATIONKEY" });

            var sqlDatabase = new SqlServerResource(resourceGroup, tenantId, currentUserObjectId);

            sqlDatabase.Build();
            keyVault.AddSecrets(sqlDatabase);
            appService.AddConfiguration(sqlDatabase, new[] { "DatabaseConnectionString" });

            appService.Build(appServicePlan);

            keyVault.AddAccessPolicy("appservice", appService.PrincipalId);

            var activeDirectory = new ActiveDirectoryResource(currentUserObjectId);
            var serviceGroup    = activeDirectory.CreateGroup("servicegroup", appService.PrincipalId);

            ServicesGroupSid = GetDatabaseSid(serviceGroup.ObjectId);
        }
Exemplo n.º 2
0
    public KeyVaultStack()
    {
        // Get current Subscription
        var currentSubscription = Output.Create(GetSubscription.InvokeAsync());
        var tenantId            = currentSubscription.Apply(currentSubscription => currentSubscription.TenantId);

        // Get current Client Config
        var currentClient = Output.Create(GetClientConfig.InvokeAsync());
        var objectId      = currentClient.Apply(currentClient => currentClient.ObjectId);

        var config            = new Pulumi.Config();
        var resourceGroupName = config.Require("resource_group_name");
        var keyVaultName      = config.Require("key_vault_name");

        // Create an Azure Resource Group
        var resourceGroup = new ResourceGroup(resourceGroupName);

        // Create an Azure Key Vault
        var keyVault = new KeyVault(keyVaultName, new KeyVaultArgs
        {
            ResourceGroupName = resourceGroup.Name,
            SkuName           = "standard",
            TenantId          = tenantId,
            AccessPolicies    =
            {
                new KeyVaultAccessPolicyArgs
                {
                    TenantId          = tenantId,
                    ObjectId          = objectId,
                    SecretPermissions ={ "list",                         "get" }
                }
            }
        });

        this.VaultUri = keyVault.VaultUri;
    }
Exemplo n.º 3
0
    public WebAppStack()
    {
        var resourceGroup = new ResourceGroup("rg-easy-azure-webapp");

        var clientConfig = Output.Create(GetClientConfig.InvokeAsync());

        var tenantId         = clientConfig.Apply(config => config.TenantId);
        var currentPrincipal = clientConfig.Apply(config => config.ObjectId);

        var solutionRoot = System.Environment.GetEnvironmentVariable("SOLUTION_ROOT_DIRECTORY");

        var storageAccount = new Account("storage", new AccountArgs
        {
            ResourceGroupName      = resourceGroup.Name,
            AccountReplicationType = "LRS",
            AccountTier            = "Standard",
        });

        var storageContainer = new Container("files", new ContainerArgs
        {
            StorageAccountName  = storageAccount.Name,
            ContainerAccessType = "private",
        });

        var codeBlob = new Blob("zip", new BlobArgs
        {
            StorageAccountName   = storageAccount.Name,
            StorageContainerName = storageContainer.Name,
            Type = "Block",

            Source = new FileArchive(Path.Join(solutionRoot, "src/Services/EasyAzureWebApp/bin/Debug/netcoreapp3.1/publish"))
        });

        var keyVault = new KeyVault("key-vault", new KeyVaultArgs
        {
            ResourceGroupName = resourceGroup.Name,
            SkuName           = "standard",
            TenantId          = tenantId,
            SoftDeleteEnabled = true,
        });

        var keyVaultPolicy = new AccessPolicy("key-vault-policy", new AccessPolicyArgs
        {
            KeyVaultId        = keyVault.Id,
            TenantId          = tenantId,
            ObjectId          = currentPrincipal,
            SecretPermissions = new[] { "delete", "get", "list", "set" },
        });

        var codeBlobSecret = new Secret("zip-secret", new SecretArgs
        {
            KeyVaultId = keyVault.Id,
            Value      = SharedAccessSignature.SignedBlobReadUrl(codeBlob, storageAccount),
        });

        var codeBlobSecretUrl = Output.All(keyVault.VaultUri, codeBlobSecret.Name, codeBlobSecret.Version)
                                .Apply(d => $"{d[0]}secrets/{d[1]}/{d[2]}");

        var appServicePlan = new Plan("easy-azure-webapp-plan", new PlanArgs
        {
            ResourceGroupName = resourceGroup.Name,
            Kind = "App",
            Sku  = new PlanSkuArgs
            {
                Tier = "Basic",
                Size = "B1",
            },
        });

        var appService = new AppService("easy-azure-webapp", new AppServiceArgs
        {
            ResourceGroupName = resourceGroup.Name,
            AppServicePlanId  = appServicePlan.Id,
            Identity          = new AppServiceIdentityArgs
            {
                Type = "SystemAssigned",
            },
            AppSettings = new InputMap <string>
            {
                { "WEBSITE_RUN_FROM_ZIP", codeBlobSecretUrl.Apply(url => $"@Microsoft.KeyVault(SecretUri={url})") },
            }
        });

        var appServiceGet = AppService.Get("easy-azure-webapp-get", appService.Id,
                                           new AppServiceState
        {
            ResourceGroupName = resourceGroup.Name,
            AppServicePlanId  = appServicePlan.Id,
        },
                                           new CustomResourceOptions
        {
            DependsOn = appService,
        }
                                           );

        var principalId = appServiceGet.Identity.Apply(id => id.PrincipalId !);

        var policy = new AccessPolicy("app-policy", new AccessPolicyArgs
        {
            KeyVaultId        = keyVault.Id,
            TenantId          = tenantId,
            ObjectId          = principalId,
            SecretPermissions = "get",
        });

        var subscriptionOutput = Output.Create(GetSubscription.InvokeAsync());
        var scope = Output.All(
            subscriptionOutput.Apply(s => s.SubscriptionId),
            resourceGroup.Name,
            storageAccount.Name,
            storageContainer.Name
            ).Apply(s => $"/subscriptions/{s[0]}/resourcegroups/{s[1]}/providers/Microsoft.Storage/storageAccounts/{s[2]}/blobServices/default/containers/{s[3]}");

        var codeBlobPermission = new Assignment("read-code-blob", new AssignmentArgs
        {
            PrincipalId        = principalId !,
            Scope              = scope,
            RoleDefinitionName = "Storage Blob Data Reader",
        },
Exemplo n.º 4
0
    static async Task <Dictionary <string, object?> > CreateResources()
    {
        var clientConfig = await GetClientConfig.InvokeAsync();

        var tenantId = clientConfig.TenantId;

        var resourceGroup = new ResourceGroup($"{ NamePrefix }-group");

        var kv = new KeyVault($"{ NamePrefix }-vault", new KeyVaultArgs {
            ResourceGroupName = resourceGroup.Name,
            SkuName           = "standard",
            TenantId          = tenantId,
            AccessPolicies    =
            {
                new KeyVaultAccessPolicyArgs {
                    TenantId = tenantId,
                    // TODO: CHANGE ME!
                    // The current principal has to be granted permissions to Key Vault so that it can actually add and then remove
                    // secrets to/from the Key Vault. Otherwise, 'pulumi up' and 'pulumi destroy' operations will fail.-                    //
                    // NOTE: This object ID value is NOT what you see in the Azure AD's App Registration screen.
                    // Run `az ad sp show` from the Azure CLI to list the correct Object ID to use here.
                    ObjectId          = "your-SP-object-ID",
                    SecretPermissions = new InputList <string>{
                        "delete", "get", "list", "set"
                    },
                }
            }
        });

        var twilioSecret = new Secret($"{ NamePrefix }-twil", new SecretArgs
        {
            KeyVaultId = kv.Id,
            Value      = TwilioAccountToken,
        });

        var appInsights = new Insights($"{ NamePrefix }-ai", new InsightsArgs
        {
            ApplicationType   = "web",
            ResourceGroupName = resourceGroup.Name,
        });

        var durableFunctionApp = new ArchiveFunctionApp($"{ NamePrefix }-funcs", new ArchiveFunctionAppArgs
        {
            ResourceGroupName = resourceGroup.Name,
            Archive           = new FileArchive($"./bin/Debug/netcoreapp3.1/GarageDoorMonitor/publish"),
            AppSettings       = new InputMap <string>
            {
                { "runtime", "dotnet" },
                { "FUNCTIONS_EXTENSION_VERSION", "~3" },
                { "TwilioAccountToken", Output.Format($"@Microsoft.KeyVault(SecretUri ={ twilioSecret.Id })") },
                { "APPINSIGHTS_INSTRUMENTATIONKEY", Output.Format($"{ appInsights.InstrumentationKey }") },
                { "TimerDelayMinutes", GetIntConfigOrDefault("timerDelayMinutes", 2) },
            },
            HttpsOnly = true,
            Identity  = new FunctionAppIdentityArgs {
                Type = "SystemAssigned"
            },
        });

        // Now that the app is created, update the access policies of the keyvault and
        // grant the principalId of the function app access to the vault.
        var principalId = durableFunctionApp.FunctionApp.Identity.Apply(id => id.PrincipalId ?? "0c4825d9-3901-40a8-ab89-ad4e3aeeadd9");

        // Grant App Service access to KV secrets
        var appAccessPolicy = new AccessPolicy($"{ NamePrefix }-app-policy", new AccessPolicyArgs
        {
            KeyVaultId        = kv.Id,
            TenantId          = tenantId,
            ObjectId          = principalId,
            SecretPermissions = new InputList <string> {
                "get"
            },
        });

        return(new Dictionary <string, object?>
        {
            { "webhookUrl", durableFunctionApp.Endpoint },
        });
    }
Exemplo n.º 5
0
    public MyStack()
    {
        var config   = new Pulumi.Config();
        var location = config.Get("location") ?? "WestUS";

        var resourceGroup = new ResourceGroup("synapse-rg");

        var storageAccount = new StorageAccount("synapsesa", new StorageAccountArgs
        {
            ResourceGroupName      = resourceGroup.Name,
            AccessTier             = AccessTier.Hot,
            EnableHttpsTrafficOnly = true,
            IsHnsEnabled           = true,
            Kind = "StorageV2",
            Sku  = new SkuArgs
            {
                Name = "Standard_RAGRS"
            },
        });
        var dataLakeStorageAccountUrl = Output.Format($"https://{storageAccount.Name}.dfs.core.windows.net");

        var users = new BlobContainer("users", new BlobContainerArgs
        {
            ResourceGroupName = resourceGroup.Name,
            AccountName       = storageAccount.Name,
            PublicAccess      = PublicAccess.None
        });

        var workspacePwd = new RandomPassword("workspace-pwd", new RandomPasswordArgs
        {
            Length = 12,
        });

        var workspace = new Workspace("workspace", new WorkspaceArgs
        {
            ResourceGroupName      = resourceGroup.Name,
            DefaultDataLakeStorage = new DataLakeStorageAccountDetailsArgs
            {
                AccountUrl = dataLakeStorageAccountUrl,
                Filesystem = "users"
            },
            Identity = new ManagedIdentityArgs
            {
                Type = ResourceIdentityType.SystemAssigned
            },
            SqlAdministratorLogin         = "******",
            SqlAdministratorLoginPassword = workspacePwd.Result
        });

        var allowAll = new IpFirewallRule("allowAll", new IpFirewallRuleArgs
        {
            ResourceGroupName = resourceGroup.Name,
            WorkspaceName     = workspace.Name,
            EndIpAddress      = "255.255.255.255",
            StartIpAddress    = "0.0.0.0"
        });

        var subscriptionId   = resourceGroup.Id.Apply(id => id.Split('/')[2]);
        var roleDefinitionId = $"/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe";

        var storageAccess = new RoleAssignment("storageAccess", new RoleAssignmentArgs
        {
            RoleAssignmentName = new RandomUuid("roleName").Result,
            Scope            = storageAccount.Id,
            PrincipalId      = workspace.Identity.Apply(identity => identity.PrincipalId).Apply(v => v ?? "<preview>"),
            PrincipalType    = "ServicePrincipal",
            RoleDefinitionId = roleDefinitionId
        });
        var clientConfig = Output.Create(GetClientConfig.InvokeAsync());
        var userAccess   = new RoleAssignment("userAccess", new RoleAssignmentArgs
        {
            RoleAssignmentName = new RandomUuid("userRoleName").Result,
            Scope            = storageAccount.Id,
            PrincipalId      = clientConfig.Apply(v => v.ObjectId),
            PrincipalType    = "User",
            RoleDefinitionId = roleDefinitionId
        });

        var sqlPool = new SqlPool("SQLPOOL1", new SqlPoolArgs
        {
            ResourceGroupName = resourceGroup.Name,
            WorkspaceName     = workspace.Name,
            Collation         = "SQL_Latin1_General_CP1_CI_AS",
            CreateMode        = "Default",
            Sku = new Pulumi.AzureNative.Synapse.Inputs.SkuArgs
            {
                Name = "DW100c"
            },
        });

        var sparkPool = new BigDataPool("Spark1", new BigDataPoolArgs
        {
            ResourceGroupName = resourceGroup.Name,
            WorkspaceName     = workspace.Name,
            AutoPause         = new AutoPausePropertiesArgs
            {
                DelayInMinutes = 15,
                Enabled        = true,
            },
            AutoScale = new AutoScalePropertiesArgs
            {
                Enabled      = true,
                MaxNodeCount = 3,
                MinNodeCount = 3,
            },
            NodeCount      = 3,
            NodeSize       = "Small",
            NodeSizeFamily = "MemoryOptimized",
            SparkVersion   = "2.4"
        });
    }
Exemplo n.º 6
0
    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" },
                        },
                    },
Exemplo n.º 7
0
    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
            }
        });
    }
Exemplo n.º 8
0
    public AppStack()
    {
        var resourceGroup = new ResourceGroup("keyvault-rg");

        // Create a storage account for Blobs
        var storageAccount = new Account("storage", new AccountArgs
        {
            ResourceGroupName      = resourceGroup.Name,
            AccountReplicationType = "LRS",
            AccountTier            = "Standard",
        });

        // The container to put our files into
        var storageContainer = new Container("files", new ContainerArgs
        {
            StorageAccountName  = storageAccount.Name,
            ContainerAccessType = "private",
        });

        // Azure SQL Server that we want to access from the application
        var administratorLoginPassword = new RandomPassword("password",
                                                            new RandomPasswordArgs {
            Length = 16, Special = true
        }).Result;
        var sqlServer = new SqlServer("sqlserver", new SqlServerArgs
        {
            ResourceGroupName = resourceGroup.Name,
            // The login and password are required but won't be used in our application
            AdministratorLogin         = "******",
            AdministratorLoginPassword = administratorLoginPassword,
            Version = "12.0",
        });

        // Azure SQL Database that we want to access from the application
        var database = new Database("db", new DatabaseArgs
        {
            ResourceGroupName             = resourceGroup.Name,
            ServerName                    = sqlServer.Name,
            RequestedServiceObjectiveName = "S0",
        });

        // The connection string that has no credentials in it: authertication will come through MSI
        var connectionString =
            Output.Format($"Server=tcp:{sqlServer.Name}.database.windows.net;Database={database.Name};");

        // A file in Blob Storage that we want to access from the application
        var textBlob = new Blob("text", new BlobArgs
        {
            StorageAccountName   = storageAccount.Name,
            StorageContainerName = storageContainer.Name,
            Type   = "Block",
            Source = new FileAsset("./README.md"),
        });

        // A plan to host the App Service
        var appServicePlan = new Plan("asp", new PlanArgs
        {
            ResourceGroupName = resourceGroup.Name,
            Kind = "App",
            Sku  = new PlanSkuArgs
            {
                Tier = "Basic",
                Size = "B1",
            },
        });

        // ASP.NET deployment package
        var blob = new Blob("zip", new BlobArgs
        {
            StorageAccountName   = storageAccount.Name,
            StorageContainerName = storageContainer.Name,
            Type   = "Block",
            Source = new FileArchive("./webapp/bin/Debug/netcoreapp3.1/publish"),
        });

        var clientConfig     = Output.Create(GetClientConfig.InvokeAsync());
        var tenantId         = clientConfig.Apply(c => c.TenantId);
        var currentPrincipal = clientConfig.Apply(c => c.ObjectId);

        // Key Vault to store secrets (e.g. Blob URL with SAS)
        var vault = new KeyVault("vault", new KeyVaultArgs
        {
            ResourceGroupName = resourceGroup.Name,
            SkuName           = "standard",
            TenantId          = tenantId,
            AccessPolicies    =
            {
                new KeyVaultAccessPolicyArgs
                {
                    TenantId = tenantId,
                    // The current principal has to be granted permissions to Key Vault so that it can actually add and then remove
                    // secrets to/from the Key Vault. Otherwise, 'pulumi up' and 'pulumi destroy' operations will fail.
                    ObjectId          = currentPrincipal,
                    SecretPermissions ={ "delete",                         "get", "list", "set" },
                }
            },
        });

        // Put the URL of the zip Blob to KV
        var secret = new Secret("deployment-zip", new SecretArgs
        {
            KeyVaultId = vault.Id,
            Value      = SharedAccessSignature.SignedBlobReadUrl(blob, storageAccount),
        });
        var secretUri = Output.Format($"{vault.VaultUri}secrets/{secret.Name}/{secret.Version}");


        // The application hosted in App Service
        var app = new AppService("app", new AppServiceArgs
        {
            ResourceGroupName = resourceGroup.Name,
            AppServicePlanId  = appServicePlan.Id,
            // A system-assigned managed service identity to be used for authentication and authorization to the SQL Database and the Blob Storage
            Identity = new AppServiceIdentityArgs {
                Type = "SystemAssigned"
            },

            AppSettings =
            {
                // Website is deployed from a URL read from the Key Vault
                { "WEBSITE_RUN_FROM_ZIP", Output.Format($"@Microsoft.KeyVault(SecretUri={secretUri})") },

                // Note that we simply provide the URL without SAS or keys
                { "StorageBlobUrl",       textBlob.Url                                                 },
            },
            ConnectionStrings =
            {
                new AppServiceConnectionStringArgs
                {
                    Name  = "db",
                    Type  = "SQLAzure",
                    Value = connectionString,
                },
            },
        });

        // Work around a preview issue https://github.com/pulumi/pulumi-azure/issues/192
        var principalId = app.Identity.Apply(id => id.PrincipalId ?? "11111111-1111-1111-1111-111111111111");

        // Grant App Service access to KV secrets
        var policy = new AccessPolicy("app-policy", new AccessPolicyArgs
        {
            KeyVaultId        = vault.Id,
            TenantId          = tenantId,
            ObjectId          = principalId,
            SecretPermissions = { "get" },
        });

        // Make the App Service the admin of the SQL Server (double check if you want a more fine-grained security model in your real app)
        var sqlAdmin = new ActiveDirectoryAdministrator("adadmin", new ActiveDirectoryAdministratorArgs
        {
            ResourceGroupName = resourceGroup.Name,
            TenantId          = tenantId,
            ObjectId          = principalId,
            Login             = "******",
            ServerName        = sqlServer.Name,
        });

        // Grant access from App Service to the container in the storage
        var blobPermission = new Assignment("readblob", new AssignmentArgs
        {
            PrincipalId        = principalId,
            Scope              = Output.Format($"{storageAccount.Id}/blobServices/default/containers/{storageContainer.Name}"),
            RoleDefinitionName = "Storage Blob Data Reader",
        });

        // Add SQL firewall exceptions
        var firewallRules = app.OutboundIpAddresses.Apply(
            ips => ips.Split(",").Select(
                ip => new FirewallRule($"FR{ip}", new FirewallRuleArgs
        {
            ResourceGroupName = resourceGroup.Name,
            StartIpAddress    = ip,
            EndIpAddress      = ip,
            ServerName        = sqlServer.Name,
        })
                ).ToList());

        this.Endpoint = Output.Format($"https://{app.DefaultSiteHostname}");
    }
Exemplo n.º 9
0
    public CdStack()
    {
        Config pulumiConfig = new();

        ResourceGroup resourceGroup = new("td-cd", new ResourceGroupArgs
        {
            ResourceGroupName = "td-cd"
        }, options : new() { ImportId = $"/subscriptions/{GetClientConfig.InvokeAsync().GetAwaiter().GetResult().SubscriptionId}/resourceGroups/td-cd" });

        var azureDevOpsVMAdminUserName     = pulumiConfig.Require($"dev-ops-vm-td-admin-user-name");
        var azureDevOpsVMAdminUserPassword = pulumiConfig.RequireSecret($"dev-ops-vm-td-admin-user-password");

        NetworkSecurityGroup azureDevOpsAgentVMNetSecurityGroup = new($"dev-ops-net-sec-group-td", new()
        {
            ResourceGroupName = resourceGroup.Name,
            Location = resourceGroup.Location,
            NetworkSecurityGroupName = $"dev-ops-net-sec-group-td",
            SecurityRules = new List <Pulumi.AzureNative.Network.Inputs.SecurityRuleArgs>
            {
                /*new Pulumi.AzureNative.Network.Inputs.SecurityRuleArgs
                 * {
                 *  Name = "RDP",
                 *  Protocol = SecurityRuleProtocol.Tcp,
                 *  SourcePortRange = "*",
                 *  DestinationPortRange = "3389",
                 *  SourceAddressPrefix ="*",
                 *  DestinationAddressPrefix = "*",
                 *  Access= SecurityRuleAccess.Allow,
                 *  Priority = 300,
                 *  Direction= SecurityRuleDirection.Inbound
                 * }*/
            }
        });

        VirtualNetwork network = new($"dev-ops-net-td", new()
        {
            ResourceGroupName = resourceGroup.Name,
            Location = resourceGroup.Location,
            VirtualNetworkName = $"dev-ops-net-td",
            AddressSpace = new AddressSpaceArgs {
                AddressPrefixes = new List <string> {
                    "10.0.0.0/16"
                }
            }
        });

        PublicIPAddress ipAddress = new($"dev-ops-ip-td", new()
        {
            ResourceGroupName = resourceGroup.Name,
            Location = resourceGroup.Location,
            PublicIpAddressName = $"dev-ops-ip-td",
            PublicIPAllocationMethod = IPAllocationMethod.Static,
            Sku = new PublicIPAddressSkuArgs {
                Name = Pulumi.AzureNative.Network.PublicIPAddressSkuName.Standard
            },
            DnsSettings = new PublicIPAddressDnsSettingsArgs
            {
                DomainNameLabel = $"dev-ops-vm-td" // dev-ops-vm-td.eastus.cloudapp.azure.com
            }
        });

        Subnet subnet = new Subnet($"dev-ops-subnet-td", new()
        {
            ResourceGroupName = resourceGroup.Name,
            Name               = $"dev-ops-subnet-td",
            SubnetName         = $"dev-ops-subnet-td",
            VirtualNetworkName = network.Name,
            AddressPrefix      = "10.0.2.0/24"
        });

        NetworkInterface networkInterface = new($"dev-ops-net-interface", new()
        {
            ResourceGroupName = resourceGroup.Name,
            Location = resourceGroup.Location,
            NetworkInterfaceName = $"dev-ops-net-interface",
            // EnableAcceleratedNetworking = true, // Depends on your vm size
            DnsSettings = new NetworkInterfaceDnsSettingsArgs
            {
                InternalDnsNameLabel = $"dev-ops-vm-td" // dev-ops-vm-td.internal.cloudapp.net
            },
            IpConfigurations = new List <NetworkInterfaceIPConfigurationArgs>
            {
                new NetworkInterfaceIPConfigurationArgs
                {
                    Name = $"dev-ops-vm-ip-td",
                    PrivateIPAddress = "10.0.0.4",
                    PrivateIPAllocationMethod = IPAllocationMethod.Dynamic,
                    PublicIPAddress = new Pulumi.AzureNative.Network.Inputs.PublicIPAddressArgs
                    {
                        Id = ipAddress.Id,
                        IpAddress = ipAddress.IpAddress
                    },
                    Subnet = new NetworkSubnetArgs {
                        Id = subnet.Id
                    },
                    Primary = true
                }
            },
            NetworkSecurityGroup = new Pulumi.AzureNative.Network.Inputs.NetworkSecurityGroupArgs
            {
                Location = resourceGroup.Location,
                Id = azureDevOpsAgentVMNetSecurityGroup.Id
            }
        });

        VirtualMachine vm = new($"dev-ops-vm-td", new()
        {
            ResourceGroupName = resourceGroup.Name,
            Location = resourceGroup.Location,
            VmName = $"dev-ops-vm-td",
            OsProfile = new Pulumi.AzureNative.Compute.Inputs.OSProfileArgs
            {
                AdminUsername = azureDevOpsVMAdminUserName,
                AdminPassword = azureDevOpsVMAdminUserPassword,
                ComputerName = $"dev-ops-vm-td",
                WindowsConfiguration = new Pulumi.AzureNative.Compute.Inputs.WindowsConfigurationArgs {
                    ProvisionVMAgent = true, EnableAutomaticUpdates = true
                }
            },
            HardwareProfile = new Pulumi.AzureNative.Compute.Inputs.HardwareProfileArgs
            {
                VmSize = VirtualMachineSizeTypes.Standard_B1ms
            },
            StorageProfile = new Pulumi.AzureNative.Compute.Inputs.StorageProfileArgs
            {
                ImageReference = new Pulumi.AzureNative.Compute.Inputs.ImageReferenceArgs {
                    Publisher = "MicrosoftWindowsServer", Offer = "WindowsServer", Sku = "2019-Datacenter", Version = "latest"
                },
                OsDisk = new Pulumi.AzureNative.Compute.Inputs.OSDiskArgs
                {
                    ManagedDisk = new Pulumi.AzureNative.Compute.Inputs.ManagedDiskParametersArgs {
                        StorageAccountType = StorageAccountTypes.Standard_LRS
                    },
                    OsType = OperatingSystemTypes.Windows,
                    Name = $"dev-ops-vm-disk-td",
                    CreateOption = DiskCreateOptionTypes.FromImage,
                    Caching = CachingTypes.ReadWrite,
                    DiskSizeGB = 127
                }
            },
            NetworkProfile = new Pulumi.AzureNative.Compute.Inputs.NetworkProfileArgs
            {
                NetworkInterfaces = new List <Pulumi.AzureNative.Compute.Inputs.NetworkInterfaceReferenceArgs>
                {
                    new Pulumi.AzureNative.Compute.Inputs.NetworkInterfaceReferenceArgs {
                        Id = networkInterface.Id
                    }
                }
            }
        });
    }