Skip to content

Commit

Permalink
Merge pull request #1629 from jemrobinson/use-pulumi-random-provider
Browse files Browse the repository at this point in the history
Use Pulumi random provider
  • Loading branch information
jemrobinson authored Oct 10, 2023
2 parents c0d88ae + 1190b3e commit bd7e91d
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 185 deletions.
10 changes: 0 additions & 10 deletions data_safe_haven/commands/deploy_shm.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,7 @@ def deploy_shm(
)
stack.add_option("azure-native:tenantId", config.azure.tenant_id, replace=False)
# Add necessary secrets
stack.add_secret("password-domain-admin", password(20), replace=False)
stack.add_secret(
"password-domain-azure-ad-connect", password(20), replace=False
)
stack.add_secret(
"password-domain-computer-manager", password(20), replace=False
)
stack.add_secret("password-domain-ldap-searcher", password(20), replace=False)
stack.add_secret(
"password-update-server-linux-admin", password(20), replace=False
)
stack.add_secret(
"verification-azuread-custom-domain", verification_record, replace=False
)
Expand Down
14 changes: 2 additions & 12 deletions data_safe_haven/commands/deploy_sre.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
DataSafeHavenError,
)
from data_safe_haven.external import GraphApi
from data_safe_haven.functions import alphanumeric, bcrypt_salt, password
from data_safe_haven.functions import alphanumeric, bcrypt_salt
from data_safe_haven.infrastructure import SHMStackManager, SREStackManager
from data_safe_haven.provisioning import SREProvisioningManager
from data_safe_haven.utility import DatabaseSystem, SoftwarePackageCategory
Expand Down Expand Up @@ -49,7 +49,7 @@ def deploy_sre(

# Initialise Pulumi stack
shm_stack = SHMStackManager(config)
stack = SREStackManager(config, sre_name)
stack = SREStackManager(config, sre_name, graph_api_token=graph_api.token)
# Set Azure options
stack.add_option("azure-native:location", config.azure.location, replace=False)
stack.add_option(
Expand Down Expand Up @@ -141,17 +141,7 @@ def deploy_sre(
)
# Add necessary secrets
stack.copy_secret("password-domain-ldap-searcher", shm_stack)
stack.add_secret("password-database-service-admin", password(20), replace=False)
stack.add_secret("password-dns-server-admin", password(20), replace=False)
stack.add_secret("password-gitea-database-admin", password(20), replace=False)
stack.add_secret(
"password-hedgedoc-database-admin", password(20), replace=False
)
stack.add_secret("password-nexus-admin", password(20), replace=False)
stack.add_secret("password-user-database-admin", password(20), replace=False)
stack.add_secret("password-workspace-admin", password(20), replace=False)
stack.add_secret("salt-dns-server-admin", bcrypt_salt(), replace=False)
stack.add_secret("token-azuread-graphapi", graph_api.token, replace=True)

# Deploy Azure infrastructure with Pulumi
if force is None:
Expand Down
10 changes: 9 additions & 1 deletion data_safe_haven/commands/teardown_sre.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
DataSafeHavenError,
DataSafeHavenInputError,
)
from data_safe_haven.external import GraphApi
from data_safe_haven.functions import alphanumeric
from data_safe_haven.infrastructure import SREStackManager

Expand All @@ -18,9 +19,16 @@ def teardown_sre(name: str) -> None:
# Load config file
config = Config()

# Load GraphAPI as this may require user-interaction that is not possible as
# part of a Pulumi declarative command
graph_api = GraphApi(
tenant_id=config.shm.aad_tenant_id,
default_scopes=["Application.ReadWrite.All", "Group.ReadWrite.All"],
)

# Remove infrastructure deployed with Pulumi
try:
stack = SREStackManager(config, sre_name)
stack = SREStackManager(config, sre_name, graph_api_token=graph_api.token)
if stack.work_dir.exists():
stack.teardown()
else:
Expand Down
10 changes: 9 additions & 1 deletion data_safe_haven/infrastructure/stack_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ def install_plugins(self) -> None:
self.stack.workspace.install_plugin(
"azure-native", metadata.version("pulumi-azure-native")
)
self.stack.workspace.install_plugin(
"random", metadata.version("pulumi-random")
)
except Exception as exc:
msg = f"Installing Pulumi plugins failed.\n{exc}."
raise DataSafeHavenPulumiError(msg) from exc
Expand Down Expand Up @@ -398,6 +401,11 @@ def __init__(
self,
config: Config,
sre_name: str,
*,
graph_api_token: str | None = None,
) -> None:
"""Constructor"""
super().__init__(config, DeclarativeSRE(config, config.shm.name, sre_name))
token = graph_api_token if graph_api_token else ""
super().__init__(
config, DeclarativeSRE(config, config.shm.name, sre_name, token)
)
1 change: 0 additions & 1 deletion data_safe_haven/infrastructure/stacks/declarative_shm.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ def run(self) -> None:
log_analytics_workspace=monitoring.log_analytics_workspace,
password_domain_admin=data.password_domain_admin,
password_domain_azuread_connect=data.password_domain_azure_ad_connect,
password_domain_computer_manager=data.password_domain_computer_manager,
password_domain_searcher=data.password_domain_searcher,
private_ip_address=networking.domain_controller_private_ip,
subnet_identity_servers=networking.subnet_identity_servers,
Expand Down
10 changes: 7 additions & 3 deletions data_safe_haven/infrastructure/stacks/declarative_sre.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@
class DeclarativeSRE:
"""Deploy with Pulumi"""

def __init__(self, config: Config, shm_name: str, sre_name: str) -> None:
def __init__(
self, config: Config, shm_name: str, sre_name: str, graph_api_token: str
) -> None:
self.cfg = config
self.graph_api_token = graph_api_token
self.shm_name = shm_name
self.sre_name = sre_name
self.short_name = f"sre-{sre_name}"
Expand Down Expand Up @@ -80,7 +83,6 @@ def run(self) -> None:
"sre_dns_server",
self.stack_name,
SREDnsServerProps(
admin_password=self.pulumi_opts.require("password-dns-server-admin"),
admin_password_salt=self.pulumi_opts.require("salt-dns-server-admin"),
location=self.cfg.azure.location,
shm_fqdn=self.cfg.shm.fqdn,
Expand Down Expand Up @@ -163,6 +165,7 @@ def run(self) -> None:
self.sre_name
].data_provider_ip_addresses,
dns_record=networking.shm_ns_record,
dns_server_admin_password=dns.password_admin,
location=self.cfg.azure.location,
networking_resource_group=networking.resource_group,
pulumi_opts=self.pulumi_opts,
Expand Down Expand Up @@ -198,7 +201,7 @@ def run(self) -> None:
SRERemoteDesktopProps(
aad_application_name=f"sre-{self.sre_name}-azuread-guacamole",
aad_application_fqdn=networking.sre_fqdn,
aad_auth_token=self.pulumi_opts.require("token-azuread-graphapi"),
aad_auth_token=self.graph_api_token,
aad_tenant_id=self.cfg.shm.aad_tenant_id,
allow_copy=self.cfg.sres[self.sre_name].remote_desktop.allow_copy,
allow_paste=self.cfg.sres[self.sre_name].remote_desktop.allow_paste,
Expand Down Expand Up @@ -313,6 +316,7 @@ def run(self) -> None:
)

# Export values for later use
pulumi.export("data", data.exports)
pulumi.export(
"ldap",
{
Expand Down
87 changes: 49 additions & 38 deletions data_safe_haven/infrastructure/stacks/shm/data.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Pulumi component for SHM state"""
from collections.abc import Mapping, Sequence

import pulumi_random
from pulumi import ComponentResource, Config, Input, Output, ResourceOptions
from pulumi_azure_native import keyvault, resources, storage

Expand All @@ -22,21 +23,9 @@ def __init__(
self.admin_group_id = admin_group_id
self.admin_ip_addresses = admin_ip_addresses
self.location = location
self.password_domain_admin = self.get_secret(
pulumi_opts, "password-domain-admin"
)
self.password_domain_azure_ad_connect = self.get_secret(
pulumi_opts, "password-domain-azure-ad-connect"
)
self.password_domain_computer_manager = self.get_secret(
pulumi_opts, "password-domain-computer-manager"
)
self.password_domain_searcher = self.get_secret(
pulumi_opts, "password-domain-ldap-searcher"
)
self.password_update_server_linux_admin = self.get_secret(
pulumi_opts, "password-update-server-linux-admin"
)
self.tenant_id = tenant_id

def get_secret(self, pulumi_opts: Config, secret_name: str) -> Output[str]:
Expand Down Expand Up @@ -138,38 +127,70 @@ def __init__(
tags=child_tags,
)

# Deploy key vault secrets
# Secret: Domain admin password
password_domain_admin = pulumi_random.RandomPassword(
f"{self._name}_password_domain_admin",
length=20,
special=True,
opts=ResourceOptions.merge(child_opts, ResourceOptions(parent=key_vault)),
)
keyvault.Secret(
f"{self._name}_kvs_password_domain_admin",
properties=keyvault.SecretPropertiesArgs(value=props.password_domain_admin),
properties=keyvault.SecretPropertiesArgs(
value=password_domain_admin.result
),
resource_group_name=resource_group.name,
secret_name="password-domain-admin",
vault_name=key_vault.name,
opts=ResourceOptions.merge(child_opts, ResourceOptions(parent=key_vault)),
opts=ResourceOptions.merge(
child_opts, ResourceOptions(parent=password_domain_admin)
),
tags=child_tags,
)

# Secret: Azure ADConnect password
password_domain_azure_ad_connect = pulumi_random.RandomPassword(
f"{self._name}_password_domain_azure_ad_connect",
length=20,
special=True,
opts=ResourceOptions.merge(child_opts, ResourceOptions(parent=key_vault)),
)
keyvault.Secret(
f"{self._name}_kvs_password_domain_azure_ad_connect",
properties=keyvault.SecretPropertiesArgs(
value=props.password_domain_azure_ad_connect
value=password_domain_azure_ad_connect.result
),
resource_group_name=resource_group.name,
secret_name="password-domain-azure-ad-connect",
vault_name=key_vault.name,
opts=ResourceOptions.merge(child_opts, ResourceOptions(parent=key_vault)),
opts=ResourceOptions.merge(
child_opts, ResourceOptions(parent=password_domain_azure_ad_connect)
),
tags=child_tags,
)

# Secret: Linux update server admin password
password_update_server_linux_admin = pulumi_random.RandomPassword(
f"{self._name}_password_update_server_linux_admin",
length=20,
special=True,
opts=ResourceOptions.merge(child_opts, ResourceOptions(parent=key_vault)),
)
keyvault.Secret(
f"{self._name}_kvs_password_domain_computer_manager",
f"{self._name}_kvs_password_update_server_linux_admin",
properties=keyvault.SecretPropertiesArgs(
value=props.password_domain_computer_manager
value=password_update_server_linux_admin.result
),
resource_group_name=resource_group.name,
secret_name="password-domain-computer-manager",
secret_name="password-update-server-linux-admin",
vault_name=key_vault.name,
opts=ResourceOptions.merge(child_opts, ResourceOptions(parent=key_vault)),
opts=ResourceOptions.merge(
child_opts, ResourceOptions(parent=password_update_server_linux_admin)
),
tags=child_tags,
)

# Add other Pulumi secrets to key vault
keyvault.Secret(
f"{self._name}_kvs_password_domain_searcher",
properties=keyvault.SecretPropertiesArgs(
Expand All @@ -181,17 +202,6 @@ def __init__(
opts=ResourceOptions.merge(child_opts, ResourceOptions(parent=key_vault)),
tags=child_tags,
)
keyvault.Secret(
f"{self._name}_kvs_password_update_server_linux_admin",
properties=keyvault.SecretPropertiesArgs(
value=props.password_update_server_linux_admin
),
resource_group_name=resource_group.name,
secret_name="password-update-server-linux-admin",
vault_name=key_vault.name,
opts=ResourceOptions.merge(child_opts, ResourceOptions(parent=key_vault)),
tags=child_tags,
)

# Deploy persistent data account
storage_account_persistent_data = storage.StorageAccount(
Expand Down Expand Up @@ -249,12 +259,13 @@ def __init__(
)

# Register outputs
self.password_domain_admin = props.password_domain_admin
self.password_domain_azure_ad_connect = props.password_domain_azure_ad_connect
self.password_domain_computer_manager = props.password_domain_computer_manager
self.password_domain_searcher = props.password_domain_searcher
self.password_update_server_linux_admin = (
props.password_update_server_linux_admin
self.password_domain_admin = Output.secret(password_domain_admin.result)
self.password_domain_azure_ad_connect = Output.secret(
password_domain_azure_ad_connect.result
)
self.password_domain_searcher = Output.secret(props.password_domain_searcher)
self.password_update_server_linux_admin = Output.secret(
password_update_server_linux_admin.result
)
self.resource_group_name = Output.from_input(resource_group.name)
self.vault = key_vault
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def __init__(
log_analytics_workspace: Input[WrappedLogAnalyticsWorkspace],
password_domain_admin: Input[str],
password_domain_azuread_connect: Input[str],
password_domain_computer_manager: Input[str],
password_domain_searcher: Input[str],
private_ip_address: Input[str],
subnet_identity_servers: Input[network.GetSubnetResult],
Expand All @@ -55,7 +54,6 @@ def __init__(
self.log_analytics_workspace = log_analytics_workspace
self.password_domain_admin = password_domain_admin
self.password_domain_azuread_connect = password_domain_azuread_connect
self.password_domain_computer_manager = password_domain_computer_manager
self.password_domain_searcher = password_domain_searcher
self.private_ip_address = private_ip_address
self.subnet_name = Output.from_input(subnet_identity_servers).apply(
Expand All @@ -65,7 +63,6 @@ def __init__(
# Note that usernames have a maximum of 20 characters
self.username_domain_admin = "dshdomainadmin"
self.username_domain_azuread_connect = "dshazureadsync"
self.username_domain_computer_manager = "dshcomputermanager"
self.username_domain_searcher = "dshldapsearcher"
self.virtual_network_name = virtual_network_name
self.virtual_network_resource_group_name = virtual_network_resource_group_name
Expand Down Expand Up @@ -136,8 +133,6 @@ def __init__(
AzureADConnectUsername=props.username_domain_azuread_connect,
DomainAdministratorPassword=props.password_domain_admin,
DomainAdministratorUsername=props.username_domain_admin,
DomainComputerManagerPassword=props.password_domain_computer_manager,
DomainComputerManagerUsername=props.username_domain_computer_manager,
DomainName=props.domain_fqdn,
DomainNetBios=props.domain_netbios_name,
DomainRootDn=props.domain_root_dn,
Expand Down
Loading

0 comments on commit bd7e91d

Please sign in to comment.