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

Pulumi: Add Git and Markdown servers #1492

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c9fd1a5
:sparkles: Add basic Gitea server
jemrobinson Jun 8, 2023
fce4311
:arrow_up: Update guacamole and guacd to 1.5.2
jemrobinson Jun 9, 2023
5817801
:adhesive_bandage: Update Pulumi resource ownership and naming structure
jemrobinson Jun 10, 2023
2c25672
:recycle: Use ldap_security_group_name instead of sre_security_group_…
jemrobinson Jun 10, 2023
28084fb
:sparkles: Create administrator and configure LDAP inside the Gitea c…
jemrobinson Jun 9, 2023
d79f1f6
:loud_sound: Update message when logging in to Microsoft Graph
jemrobinson Jun 19, 2023
1683a11
:sparkles: Add private DNS which routes gitea.privatelink.<SRE FQDN> …
jemrobinson Jun 19, 2023
60ef150
:lock: Ensure that user passwords do not expire to match change made …
jemrobinson Jun 20, 2023
4780d39
:sparkles: Add redirect from public to private DNS
jemrobinson Jun 21, 2023
a55ec11
:truck: Moved Gitea into subcomponent
jemrobinson Jun 21, 2023
1cab4e0
:truck: Updated component naming structure
jemrobinson Jun 21, 2023
f0ad651
:truck: Set correct parent for resources under SRE data
jemrobinson Jun 22, 2023
40d1abd
:recycle: Split user services into two subnets
jemrobinson Jun 22, 2023
0b5bd28
:wrench: Add transformation to get IP addresses from private endpoint
jemrobinson Jun 22, 2023
d0b642e
:closed_lock_with_key: Add password_hedgedoc_database_admin
jemrobinson Jun 22, 2023
1f43f83
:recycle: Add initial HedgeDoc server
jemrobinson Jun 22, 2023
c4670c0
:sparkles: Add LDAP login for HedgeDoc server
jemrobinson Jun 22, 2023
8c077bf
:goal_net: Improve exception handling
jemrobinson Jun 22, 2023
8e1ad6c
:goal_net: Improve exception handling
jemrobinson Jun 22, 2023
a221255
:sparkles: Use PostgreSQL backend for Gitea
jemrobinson Jun 22, 2023
e89f4b0
:wrench: Use structured arguments instead of dictionary in database c…
jemrobinson Jun 26, 2023
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
8 changes: 5 additions & 3 deletions data_safe_haven/commands/deploy_shm_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,13 @@ def handle(self) -> int:
return 0

except DataSafeHavenException as exc:
error_msg = (
exception_text = (
f"Could not deploy Data Safe Haven Management environment.\n{str(exc)}"
)
for line in error_msg.split("\n"):
self.error(line)
except Exception as exc:
exception_text = f"Uncaught exception of type '{type(exc)}'.\n{str(exc)}"
for line in exception_text.split("\n"):
self.error(line)
return 1

def add_missing_values(self, config: Config) -> None:
Expand Down
18 changes: 12 additions & 6 deletions data_safe_haven/commands/deploy_sre_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ def handle(self) -> int:
shm_stack.output("domain_controllers")["ldap_server_ip"],
True,
)
stack.add_option(
"shm-domain_controllers-netbios_name",
shm_stack.output("domain_controllers")["netbios_name"],
True,
)
stack.add_option(
"shm-monitoring-automation_account_name",
shm_stack.output("monitoring")["automation_account_name"],
Expand Down Expand Up @@ -156,6 +161,8 @@ def handle(self) -> int:
)
# Add necessary secrets
stack.copy_secret("password-domain-ldap-searcher", shm_stack)
stack.add_secret("password-gitea-database-admin", password(20))
stack.add_secret("password-hedgedoc-database-admin", password(20))
stack.add_secret("password-user-database-admin", password(20))
stack.add_secret("password-secure-research-desktop-admin", password(20))
stack.add_secret("token-azuread-graphapi", graph_api.token, replace=True)
Expand Down Expand Up @@ -184,12 +191,11 @@ def handle(self) -> int:
return 0

except DataSafeHavenException as exc:
for (
line
) in f"Could not deploy Secure Research Environment {self.sre_name}.\n{str(exc)}".split(
"\n"
):
self.error(line)
exception_text = f"Could not deploy Secure Research Environment {self.sre_name}.\n{str(exc)}"
except Exception as exc:
exception_text = f"Uncaught exception of type '{type(exc)}'.\n{str(exc)}"
for line in exception_text.split("\n"):
self.error(line)
return 1

def add_missing_values(self, config: Config) -> None:
Expand Down
2 changes: 1 addition & 1 deletion data_safe_haven/external/api/graph_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ def create_token_administrator(self) -> str:
f"Please sign-in with <fg=green>global administrator</> credentials for Azure Active Directory <fg=green>{self.tenant_id}</>."
)
self.info(
"Note that the sign-in screen will prompt you to sign-in to <fg=blue>Microsoft Graph Powershell</> - this is expected."
"Note that the sign-in screen will prompt you to sign-in to <fg=blue>Microsoft Graph Command Line Tools</> - this is expected."
)
self.info(flow["message"])
# Block until a response is received
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def run_executable(self, container_name: str, executable_path: str) -> List[str]
"""
Run a script or command on one of the containers.

The command cannot take any arguments and must be a single expression.
It is possible to provide arguments to the command if needed.
The most likely use-case is running a script already present in the container.
"""
# Connect to Azure clients
Expand Down
24 changes: 12 additions & 12 deletions data_safe_haven/provisioning/sre_provisioning_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __init__(
"resource_group_name": shm_stack.output("domain_controllers")[
"resource_group_name"
],
"group_name": sre_stack.output("research_desktops")["security_group_name"],
"group_name": sre_stack.output("ldap")["security_group_name"],
"vm_name": shm_stack.output("domain_controllers")["vm_name"],
}

Expand Down Expand Up @@ -76,6 +76,17 @@ def create_security_group(self) -> None:
for line in output.split("\n"):
self.parse_as_log(line)

def restart_remote_desktop_containers(self) -> None:
# Restart the Guacamole container group
guacamole_provisioner = AzureContainerInstance(
self.remote_desktop_params["container_group_name"],
self.remote_desktop_params["resource_group_name"],
self.subscription_name,
)
guacamole_provisioner.restart(
self.remote_desktop_params["container_ip_address"]
)

def update_remote_desktop_connections(self) -> None:
"""Update connection information on the Guacamole PostgreSQL server"""
postgres_provisioner = AzurePostgreSQLDatabase(
Expand Down Expand Up @@ -115,17 +126,6 @@ def update_remote_desktop_connections(self) -> None:
mustache_values=connection_data,
)

def restart_remote_desktop_containers(self) -> None:
# Restart the Guacamole container group
guacamole_provisioner = AzureContainerInstance(
self.remote_desktop_params["container_group_name"],
self.remote_desktop_params["resource_group_name"],
self.subscription_name,
)
guacamole_provisioner.restart(
self.remote_desktop_params["container_ip_address"]
)

def run(self) -> None:
"""Apply SRE configuration"""
self.create_security_group()
Expand Down
1 change: 1 addition & 0 deletions data_safe_haven/pulumi/common/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class NetworkingPriorities(int, Enum):
INTERNAL_SHM_UPDATE_SERVERS = 1400
INTERNAL_SRE_PRIVATE_DATA = 1500
INTERNAL_SRE_REMOTE_DESKTOP = 1600
INTERNAL_SRE_USER_SERVICES = 1700
INTERNAL_DSH_VIRTUAL_NETWORK = 1999
# Authorised external IPs: 2000-2999
AUTHORISED_EXTERNAL_ADMIN_IPS = 2000
Expand Down
23 changes: 20 additions & 3 deletions data_safe_haven/pulumi/common/transformations.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def get_id_from_rg(rg: resources.ResourceGroup) -> Input[str]:
"""Get the ID of a resource group"""
if isinstance(rg.id, Output):
return rg.id
raise DataSafeHavenPulumiException(f"Resource group '{rg.name}'has no ID.")
raise DataSafeHavenPulumiException(f"Resource group '{rg.name}' has no ID.")


def get_id_from_subnet(subnet: network.GetSubnetResult) -> str:
Expand All @@ -32,11 +32,28 @@ def get_id_from_subnet(subnet: network.GetSubnetResult) -> str:
raise DataSafeHavenPulumiException(f"Subnet '{subnet.name}' has no ID.")


def get_ip_addresses_from_private_endpoint(
endpoint: network.PrivateEndpoint,
) -> Input[List[str]]:
"""Get a list of IP addresses from a private endpoint"""
if isinstance(endpoint.custom_dns_configs, Output):
return endpoint.custom_dns_configs.apply(
lambda cfgs: sum(
[list(cfg.ip_addresses) if cfg.ip_addresses else [] for cfg in cfgs], []
)
if cfgs
else []
)
raise DataSafeHavenPulumiException(
f"Private endpoint '{endpoint.name}' has no IP addresses."
)


def get_name_from_rg(rg: resources.ResourceGroup) -> Input[str]:
"""Get the name of a resource group"""
if isinstance(rg.name, Output):
return rg.name.apply(lambda s: str(s))
raise DataSafeHavenPulumiException(f"Resource group '{rg.id}'has no name.")
raise DataSafeHavenPulumiException(f"Resource group '{rg.id}' has no name.")


def get_name_from_subnet(subnet: network.GetSubnetResult) -> str:
Expand All @@ -50,4 +67,4 @@ def get_name_from_vnet(vnet: network.VirtualNetwork) -> Input[str]:
"""Get the ID of a virtual network"""
if isinstance(vnet.name, Output):
return vnet.name.apply(lambda s: str(s))
raise DataSafeHavenPulumiException(f"Virtual network '{vnet.id}'has no name.")
raise DataSafeHavenPulumiException(f"Virtual network '{vnet.id}' has no name.")
2 changes: 1 addition & 1 deletion data_safe_haven/pulumi/components/shm_bastion.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(
props: SHMBastionProps,
opts: Optional[ResourceOptions] = None,
):
super().__init__("dsh:shm:SHMBastionComponent", name, {}, opts)
super().__init__("dsh:shm:BastionComponent", name, {}, opts)
child_opts = ResourceOptions.merge(ResourceOptions(parent=self), opts)

# Deploy IP address
Expand Down
2 changes: 1 addition & 1 deletion data_safe_haven/pulumi/components/shm_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def __init__(
props: SHMDataProps,
opts: Optional[ResourceOptions] = None,
):
super().__init__("dsh:shm:SHMDataComponent", name, {}, opts)
super().__init__("dsh:shm:DataComponent", name, {}, opts)
child_opts = ResourceOptions.merge(ResourceOptions(parent=self), opts)

# Deploy resource group
Expand Down
3 changes: 2 additions & 1 deletion data_safe_haven/pulumi/components/shm_domain_controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def __init__(
props: SHMDomainControllersProps,
opts: Optional[ResourceOptions] = None,
):
super().__init__("dsh:shm:SHMDomainControllersComponent", name, {}, opts)
super().__init__("dsh:shm:DomainControllersComponent", name, {}, opts)
child_opts = ResourceOptions.merge(ResourceOptions(parent=self), opts)
resources_path = pathlib.Path(__file__).parent.parent.parent / "resources"

Expand Down Expand Up @@ -195,6 +195,7 @@ def __init__(
"ldap_root_dn": props.domain_root_dn,
"ldap_search_username": props.username_domain_searcher,
"ldap_server_ip": primary_domain_controller.ip_address_private,
"netbios_name": props.domain_netbios_name,
"resource_group_name": resource_group.name,
"vm_name": primary_domain_controller.vm_name,
}
2 changes: 1 addition & 1 deletion data_safe_haven/pulumi/components/shm_firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __init__(
props: SHMFirewallProps,
opts: Optional[ResourceOptions] = None,
):
super().__init__("dsh:shm:SHMFirewallComponent", name, {}, opts)
super().__init__("dsh:shm:FirewallComponent", name, {}, opts)
child_opts = ResourceOptions.merge(ResourceOptions(parent=self), opts)

# Important IP addresses
Expand Down
2 changes: 1 addition & 1 deletion data_safe_haven/pulumi/components/shm_monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __init__(
props: SHMMonitoringProps,
opts: Optional[ResourceOptions] = None,
):
super().__init__("dsh:shm:SHMMonitoringComponent", name, {}, opts)
super().__init__("dsh:shm:MonitoringComponent", name, {}, opts)
child_opts = ResourceOptions.merge(ResourceOptions(parent=self), opts)

# Deploy resource group
Expand Down
3 changes: 2 additions & 1 deletion data_safe_haven/pulumi/components/shm_networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def __init__(
props: SHMNetworkingProps,
opts: Optional[ResourceOptions] = None,
):
super().__init__("dsh:shm:SHMNetworkingComponent", name, {}, opts)
super().__init__("dsh:shm:NetworkingComponent", name, {}, opts)
child_opts = ResourceOptions.merge(ResourceOptions(parent=self), opts)

# Deploy resource group
Expand Down Expand Up @@ -274,6 +274,7 @@ def __init__(
network_security_group_name=f"{stack_name}-nsg-identity",
resource_group_name=resource_group.name,
security_rules=[
# Inbound
network.SecurityRuleArgs(
access=network.SecurityRuleAccess.ALLOW,
description="Allow inbound LDAP to domain controllers.",
Expand Down
2 changes: 1 addition & 1 deletion data_safe_haven/pulumi/components/shm_update_servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __init__(
props: SHMUpdateServersProps,
opts: Optional[ResourceOptions] = None,
):
super().__init__("dsh:shm:SHMUpdateServersComponent", name, {}, opts)
super().__init__("dsh:shm:UpdateServersComponent", name, {}, opts)
child_opts = ResourceOptions.merge(ResourceOptions(parent=self), opts)

# Load cloud-init file
Expand Down
Loading