Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure cs sqlserver privateendpoint vnet injection #1177

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions azure-cs-sqlserver-privateendpoint-vnet-injection/MyStack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Copyright 2016-2022, Pulumi Corporation. All rights reserved.

using System;
using System.Threading.Tasks;
using Pulumi;
using AzureNative = Pulumi.AzureNative;
using Resources = Pulumi.AzureNative.Resources;
using Sql = Pulumi.AzureNative.Sql;
using Pulumi.Random;

class MyStack : Stack
{
public MyStack()
{
var resourceGroup = new Resources.ResourceGroup("resourceGroup");

var password = new Pulumi.Random.RandomPassword("admin-password", new Pulumi.Random.RandomPasswordArgs { Length = 20 });

Sql.Server server = new Sql.Server(
"server",
new Sql.ServerArgs
{
AdministratorLogin = "admin-user",
AdministratorLoginPassword = password.Result,
ResourceGroupName = resourceGroup.Name,
ServerName = $"{Pulumi.Deployment.Instance.StackName}",
MinimalTlsVersion = "1.2",
PublicNetworkAccess = "Disabled"
});

this.ServerName = server.Name.Apply(servername => $"{servername}.database.windows.net");

Sql.Database database = new Sql.Database(
"db",
new Sql.DatabaseArgs
{
DatabaseName = "database",
ServerName = server.Name,
Collation = "SQL_Latin1_General_CP1_CI_AI",
ResourceGroupName = resourceGroup.Name,
Sku = new AzureNative.Sql.Inputs.SkuArgs
{
Capacity = 2,
Family = "Gen5",
Name = "GP_S", /*Serverless*/
}
});

var vnet = new AzureNative.Network.VirtualNetwork("SQLServer-network", new AzureNative.Network.VirtualNetworkArgs
{
VirtualNetworkName = "SQLServer-network",
ResourceGroupName = resourceGroup.Name,
AddressSpace = new AzureNative.Network.Inputs.AddressSpaceArgs
{
AddressPrefixes = new[] { "10.0.0.0/8" }
}
});

var subnet = new AzureNative.Network.Subnet("SQLServer-subnet", new AzureNative.Network.SubnetArgs
{
Name = "SQLServer-subnet",
ResourceGroupName = resourceGroup.Name,
VirtualNetworkName = vnet.Name,
AddressPrefix = "10.0.0.0/16",
PrivateEndpointNetworkPolicies = "Disabled"
});

string privateEndpointName = "SQLServer-PrivateEndpoint";
var privateEndpoint = new AzureNative.Network.PrivateEndpoint(privateEndpointName, new AzureNative.Network.PrivateEndpointArgs
{
ResourceGroupName = resourceGroup.Name,
PrivateEndpointName = privateEndpointName,
PrivateLinkServiceConnections =
{
new AzureNative.Network.Inputs.PrivateLinkServiceConnectionArgs
{
GroupIds =
{
"sqlServer",
},
Name = $"{privateEndpointName}-PrivateLinkServiceConnection",
PrivateLinkServiceId = server.Id,
},
},
Subnet = new AzureNative.Network.Inputs.SubnetArgs
{
Id = subnet.Id,
},
});

var privateZone = new AzureNative.Network.PrivateZone($"sqlserver-privateZone", new AzureNative.Network.PrivateZoneArgs
{
PrivateZoneName = server.Name.Apply(servername => servername + ".privatelink.database.windows.net"),
ResourceGroupName = resourceGroup.Name,
Location = "global",
});

var privateRecordSet = new AzureNative.Network.PrivateRecordSet($"sqlserver-privateRecordSet", new AzureNative.Network.PrivateRecordSetArgs
{
ARecords =
{
new AzureNative.Network.Inputs.ARecordArgs
{
Ipv4Address = Output.Tuple(resourceGroup.Name, privateEndpoint.Name)
.Apply(names =>
{
return GetPrivateEndpointIP(names.Item1, names.Item2);
}),
},
},
PrivateZoneName = privateZone.Name,
RecordType = "A",
RelativeRecordSetName = "@",
ResourceGroupName = resourceGroup.Name,
Ttl = 3600,
});

string virtualNetworkLinkName = $"sqlserver-VirtualNetworkLink";
var virtualNetworkLink = new AzureNative.Network.VirtualNetworkLink(virtualNetworkLinkName, new AzureNative.Network.VirtualNetworkLinkArgs
{
PrivateZoneName = privateZone.Name,
ResourceGroupName = resourceGroup.Name,
RegistrationEnabled = false,
Location = "global",
VirtualNetwork = new AzureNative.Network.Inputs.SubResourceArgs
{
Id = vnet.Id,
},
VirtualNetworkLinkName = virtualNetworkLinkName
});

var privateDnsZoneGroup = new AzureNative.Network.PrivateDnsZoneGroup($"sqlserver-PrivateDnsZoneGroup", new AzureNative.Network.PrivateDnsZoneGroupArgs
{
PrivateDnsZoneConfigs =
{
new AzureNative.Network.Inputs.PrivateDnsZoneConfigArgs
{
Name = privateZone.Name,
PrivateDnsZoneId = privateZone.Id,
},
},
PrivateDnsZoneGroupName = privateEndpoint.Name,
PrivateEndpointName = privateEndpoint.Name,
ResourceGroupName = resourceGroup.Name,
});

}

public static async Task<string> GetPrivateEndpointIP(string resourceGroupName, string privateEndpointName)
{
/* Azure api does not fill out the Name property of the NetworkInterfaces object nor does it expand to load the PrivateIPAddress under NetworkInterfaces.
Azure api only fill out the Id property of the NetworkInterfaces object
In order to get the PrivateIPAddress we need to call GetNetworkInterface.. which requires the NetworkInterface Name
Thus we need to derived the NetworkInterfaces Name from its Id
*/
Pulumi.Log.Debug("GetPrivateEndpointIP for " + privateEndpointName);
var privateEndpoint = await AzureNative.Network.GetPrivateEndpoint.InvokeAsync(new AzureNative.Network.GetPrivateEndpointArgs
{
PrivateEndpointName = privateEndpointName,
ResourceGroupName = resourceGroupName,
});
string nicName = privateEndpoint.NetworkInterfaces[0].Id!;
nicName = nicName.Remove(0, nicName.LastIndexOf(@"/") + 1);
if (privateEndpoint != null && nicName != string.Empty)
{
var nic = await AzureNative.Network.GetNetworkInterface.InvokeAsync(new AzureNative.Network.GetNetworkInterfaceArgs
{
NetworkInterfaceName = nicName,
ResourceGroupName = resourceGroupName,
Expand = "true",
});
if (nic != null && nic.IpConfigurations != null)
{
return nic.IpConfigurations[0].PrivateIPAddress!;
}
}
return string.Empty;
}

[Output("serverName")]
public Output<string> ServerName { get; set; }

}
9 changes: 9 additions & 0 deletions azure-cs-sqlserver-privateendpoint-vnet-injection/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright 2016-2021, Pulumi Corporation. All rights reserved.

using System.Threading.Tasks;
using Pulumi;

class Program
{
static Task<int> Main() => Deployment.RunAsync<MyStack>();
}
3 changes: 3 additions & 0 deletions azure-cs-sqlserver-privateendpoint-vnet-injection/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: azure-cs-sqlserver
runtime: dotnet
description: An example of a SQLServer on Azure PaaS
44 changes: 44 additions & 0 deletions azure-cs-sqlserver-privateendpoint-vnet-injection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new?template=https://github.com/pulumi/examples/blob/master/azure-cs-sqlserver-privateendpoint-vnet-injection/README.md)

# A SQLServer on Azure PaaS secured using a private endpoint

This example configures [An example of a SQLServer on Azure PaaS connected to a subnet using a private endpoint](https://docs.microsoft.com/en-us/azure/private-link/private-endpoint-overview).

In addition to the server itself, a database is configured
The server is not exposed to the internet and to connect to it you have to use a ressource in the subnet.
A private DNS Zone is configured to point the server record to the private IP (servername.database.windows.net)

## Running the App

1. Create a new stack:

```
$ pulumi stack init dev
```

1. Login to Azure CLI (you will be prompted to do this during deployment if you forget this step):

```
$ az login
```
1. Set the Azure region location to use:

```
$ pulumi config set azure-native:location westus
```

1. Run `pulumi up` to preview and deploy changes:

```
$ pulumi up
Previewing changes:
...

Performing changes:
...
Resources:
+ 12 created
Duration: 5m16s
```

1. Check the deployed sql server and the private endpoint configuration.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Pulumi" Version="3.*" />
<PackageReference Include="Pulumi.AzureNative" Version="1.*" />
<PackageReference Include="Pulumi.Docker" Version="2.*" />
<PackageReference Include="Pulumi.Random" Version="4.*" />
</ItemGroup>

</Project>