Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8a6a522
NRL-1385 initial ec2 stuff
jackleary May 1, 2025
8483dd2
NRL-1385 base ec2 set up
jackleary May 2, 2025
37a2e8a
NRL-1385 use autoscaling ec2 for flexibility
jackleary May 7, 2025
b386a49
nrl-1385 Merge branch 'develop' into feature/jale13-nrl-1385-set-up-p…
jackleary May 7, 2025
70c2e55
NRL-1385 create ec2 instance
jackleary May 11, 2025
bd836f3
NRL-1385 create vpc module
jackleary May 11, 2025
b7bf446
NRL-1385 call modules
jackleary May 11, 2025
4aebec2
NRL-1385 add tags
jackleary May 11, 2025
5464f16
NRL-1385 syntax fixes
jackleary May 11, 2025
d80addc
NRL-1385 create key pair to rdp into ec2
jackleary May 11, 2025
d9fa802
NRL-1385 update script
jackleary May 11, 2025
20daf5a
NRL-1385 update script
jackleary May 11, 2025
b873b7a
NRL-1385 security fix
jackleary May 11, 2025
f8dc520
NRL-1385 security fix
jackleary May 11, 2025
1f027c9
NRL-1385 make public temporarily. Will revert later
jackleary May 11, 2025
779b838
NRL-1385 sort iam policies to allow ec2 access to Athena
jackleary May 15, 2025
77a7179
NRL-1385 remove unused script
jackleary May 16, 2025
1f73f2c
nrl-1385 Merge remote-tracking branch 'origin' into feature/jale13-nr…
jackleary May 27, 2025
387342e
NRL-1385 Make VPC private
jackleary May 28, 2025
dcce041
NRL-1385 Point EC2 at custom AMI
jackleary May 28, 2025
1ea3038
NRL-1385 Update readme
jackleary May 28, 2025
9e72ff3
NRL-1385 rmeove public ip from ec2
jackleary May 28, 2025
1bbcbd3
NRL-1385 remove unused security group
jackleary May 28, 2025
f8485c0
NRL-1385 Spelling
jackleary May 29, 2025
2b1e371
NRL-1385 remove invocation of init script in user data
jackleary May 29, 2025
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
14 changes: 14 additions & 0 deletions terraform/account-wide-infrastructure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,20 @@ $ terraform apply \

Replacing AWS_ACCOUNT_ID with the AWS account number of your account.

### Reporting Resources

If deploying the EC2 set up to a new environment, these steps need to be followed:

1. Run the below CLI command, and RDP into the newly created EC2 instance (localhost:13389)

```
aws ssm start-session --target <AMI> --document-name AWS-StartPortForwardingSession --parameters "localPortNumber=13389,portNumber=3389"
```

2. Install Athena ODBC driver and Power BI personal on premises gateway
3. Configure ODBC driver to connect to relevant Athena instance and log in to the gateway using NHS email
4. Log into power bi and test the refresh on the relevant data sources

## Tear down account wide resources

WARNING - This action will destroy all account-wide resources from the AWS account. This should
Expand Down
23 changes: 23 additions & 0 deletions terraform/account-wide-infrastructure/dev/ec2.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module "vpc" {
source = "../modules/vpc"
vpc_cidr_block = var.vpc_cidr_block
enable_dns_hostnames = var.enable_dns_hostnames
vpc_public_subnets_cidr_block = var.vpc_public_subnets_cidr_block
vpc_private_subnets_cidr_block = var.vpc_private_subnets_cidr_block
aws_azs = var.aws_azs
name_prefix = "nhsd-nrlf--dev"
}

module "powerbi_gw_instance_v2" {
source = "../modules/ec2"
use_custom_ami = true
instance_type = var.instance_type
name_prefix = "nhsd-nrlf--dev-powerbi-gw-v2"
target_bucket_arn = module.dev-glue.target_bucket_arn
glue_kms_key_arn = module.dev-glue.aws_kms_key_arn
athena_kms_key_arn = module.dev-athena.kms_key_arn
athena_bucket_arn = module.dev-athena.bucket_arn

subnet_id = module.vpc.private_subnet_id
security_groups = [module.vpc.powerbi_gw_security_group_id]
}
42 changes: 42 additions & 0 deletions terraform/account-wide-infrastructure/dev/vars.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,45 @@ variable "devsandbox_api_domain_name" {
description = "The internal DNS name of the API Gateway for the dev sandbox environment"
default = "dev-sandbox.api.record-locator.dev.national.nhs.uk"
}

variable "aws_azs" {
type = string
description = "AWS Availability Zones"
default = "eu-west-2a"
}

variable "enable_dns_hostnames" {
type = bool
description = "Enable DNS hostnames in VPC"
default = true
}

variable "vpc_cidr_block" {
type = string
description = "Base CIDR Block for VPC"
default = "10.0.0.0/16"
}

variable "vpc_public_subnets_cidr_block" {
type = string
description = "CIDR Block for Public Subnets in VPC"
default = "10.0.0.0/24"
}

variable "vpc_private_subnets_cidr_block" {
type = string
description = "CIDR Block for Private Subnets in VPC"
default = "10.0.1.0/24"
}

variable "instance_type" {
type = string
description = "Type for EC2 Instance"
default = "t2.micro"
}

variable "use_custom_ami" {
type = bool
description = "Use custom image"
default = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ output "workgroup" {
output "bucket" {
value = aws_s3_bucket.athena
}

output "bucket_arn" {
value = aws_s3_bucket.athena.arn
}

output "kms_key_arn" {
value = aws_kms_key.athena.arn
}
17 changes: 17 additions & 0 deletions terraform/account-wide-infrastructure/modules/athena/s3.tf
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@ resource "aws_s3_bucket_policy" "athena" {
}
}
},
{
Sid : "AllowAthenaAccess",
Effect : "Allow",
Principal : {
Service : "athena.amazonaws.com"
},
Action : [
"s3:PutObject",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket"
],
Resource : [
aws_s3_bucket.athena.arn,
"${aws_s3_bucket.athena.arn}/*",
]
},
]
})
}
Expand Down
17 changes: 17 additions & 0 deletions terraform/account-wide-infrastructure/modules/ec2/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
data "aws_ami" "windows-2019" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["Windows_Server-2019-English-Full-Base*"]
}
}

data "aws_ami" "PowerBI_Gateway" {
most_recent = true
owners = ["self"]
filter {
name = "name"
values = ["PowerBI_GW"]
}
}
30 changes: 30 additions & 0 deletions terraform/account-wide-infrastructure/modules/ec2/ec2.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
resource "aws_instance" "web" {
associate_public_ip_address = false
iam_instance_profile = aws_iam_instance_profile.powerbi_profile.name
ami = local.selected_ami_id
instance_type = var.instance_type
key_name = aws_key_pair.ec2_key_pair.key_name
subnet_id = var.subnet_id
security_groups = var.security_groups

user_data = file("${path.module}/scripts/user_data.tpl")

tags = {
Name = "${var.name_prefix}-ec2"
}

}

resource "tls_private_key" "instance_key_pair" {
algorithm = "RSA"
}

resource "aws_key_pair" "ec2_key_pair" {
key_name = "${var.name_prefix}_PowerBI-GateWay-Key"
public_key = tls_private_key.instance_key_pair.public_key_openssh
}

resource "local_file" "ssh_key_priv" {
filename = "${path.module}/keys/${aws_key_pair.ec2_key_pair.key_name}.pem"
content = tls_private_key.instance_key_pair.private_key_pem
}
110 changes: 110 additions & 0 deletions terraform/account-wide-infrastructure/modules/ec2/iam.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
resource "aws_iam_role" "ec2_service_role" {
name = "${var.name_prefix}-ec2_service_role"

assume_role_policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Principal" : {
"Service" : "ec2.amazonaws.com"
},
"Action" : "sts:AssumeRole"
}
]
})
}

data "aws_iam_policy_document" "ec2_service" {
statement {
actions = [
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:ListMultipartUploadParts",
"s3:AbortMultipartUpload",
"s3:CreateBucket",
"s3:PutObject",
"s3:PutBucketPublicAccessBlock"
]

resources = compact([
var.target_bucket_arn,
"${var.target_bucket_arn}/*",
var.athena_bucket_arn,
"${var.athena_bucket_arn}/*",
])
effect = "Allow"
}

statement {
actions = [
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:ListAllMyBuckets"
]

resources = compact([
"*"
])
effect = "Allow"
}

statement {
actions = [
"kms:DescribeKey",
"kms:GenerateDataKey*",
"kms:Encrypt",
"kms:ReEncrypt*",
"kms:Decrypt",
]

resources = [
var.glue_kms_key_arn,
var.athena_kms_key_arn,
]

effect = "Allow"
}

statement {
actions = [
"athena:*",
]
effect = "Allow"
resources = [
"*"
]
}

statement {
actions = [
"glue:*",
]
effect = "Allow"
resources = [
"*"
]
}
}

resource "aws_iam_policy" "ec2_service" {
name = "${var.name_prefix}-ec2"
policy = data.aws_iam_policy_document.ec2_service.json
}

resource "aws_iam_role_policy_attachment" "ec2_role_policy" {
role = aws_iam_role.ec2_service_role.name
policy_arn = aws_iam_policy.ec2_service.arn
}

resource "aws_iam_role_policy_attachment" "ec2_role_policy_ssm" {
role = aws_iam_role.ec2_service_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_instance_profile" "powerbi_profile" {
name = "${var.name_prefix}-powerbi_instance_profile"
role = aws_iam_role.ec2_service_role.name
}
3 changes: 3 additions & 0 deletions terraform/account-wide-infrastructure/modules/ec2/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
locals {
selected_ami_id = var.use_custom_ami ? data.aws_ami.PowerBI_Gateway.id : data.aws_ami.windows-2019.id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "instance_id" {
value = aws_instance.web.id
}

output "public_ip" {
value = aws_instance.web.public_ip
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@


<powershell>
Install-WindowsFeature -name Web-Server -IncludeManagementTools

$instanceId = (Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/instance-id -UseBasicParsing).content
$instanceAZ = (Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/placement/availability-zone -UseBasicParsing).content
$pubHostName = (Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/public-hostname -UseBasicParsing).content
$pubIPv4 = (Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/public-ipv4 -UseBasicParsing).content
$privHostName = (Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/local-hostname -UseBasicParsing).content
$privIPv4 = (Invoke-WebRequest -Uri http://169.254.169.254/latest/meta-data/local-ipv4 -UseBasicParsing).content

New-Item -Path C:\inetpub\wwwroot\index.html -ItemType File -Force
Add-Content -Path C:\inetpub\wwwroot\index.html "<font face = "Verdana" size = "5">"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center><h1>AWS Windows VM Deployed with Terraform</h1></center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>EC2 Instance Metadata</b> </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Instance ID:</b> $instanceId </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>AWS Availablity Zone:</b> $instanceAZ </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Public Hostname:</b> $pubHostName </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Public IPv4:</b> $pubIPv4 </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Private Hostname:</b> $privHostName </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "<center> <b>Private IPv4:</b> $privIPv4 </center>"
Add-Content -Path C:\inetpub\wwwroot\index.html "</font>"

</powershell>
9 changes: 9 additions & 0 deletions terraform/account-wide-infrastructure/modules/ec2/vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
variable "name_prefix" {}
variable "instance_type" {}
variable "security_groups" {}
variable "subnet_id" {}
variable "glue_kms_key_arn" {}
variable "athena_kms_key_arn" {}
variable "target_bucket_arn" {}
variable "athena_bucket_arn" {}
variable "use_custom_ami" {}
10 changes: 10 additions & 0 deletions terraform/account-wide-infrastructure/modules/glue/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,21 @@ output "target_bucket_name" {
value = aws_s3_bucket.target-data-bucket.id
}

output "target_bucket_arn" {
description = "Arn of destination bucket"
value = aws_s3_bucket.target-data-bucket.arn
}

output "source_bucket_name" {
description = "Name of source bucket"
value = aws_s3_bucket.source-data-bucket.id
}

output "aws_kms_key_arn" {
description = "Arn of kms key"
value = aws_kms_key.glue.arn
}

output "glue_crawler_name" {
value = "s3//${aws_s3_bucket.source-data-bucket.id}/"
}
11 changes: 11 additions & 0 deletions terraform/account-wide-infrastructure/modules/vpc/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
output "subnet_id" {
value = aws_subnet.public_subnet.id
}

output "private_subnet_id" {
value = aws_subnet.private_subnet.id
}

output "powerbi_gw_security_group_id" {
value = aws_security_group.powerbi_gw_sg.id
}
6 changes: 6 additions & 0 deletions terraform/account-wide-infrastructure/modules/vpc/vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
variable "aws_azs" {}
variable "enable_dns_hostnames" {}
variable "vpc_cidr_block" {}
variable "vpc_public_subnets_cidr_block" {}
variable "vpc_private_subnets_cidr_block" {}
variable "name_prefix" {}
Loading
Loading