Skip to content
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
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
nodejs 18.12.1
terraform 1.13.3
trivy 0.47.0
trivy 0.69.2
2 changes: 2 additions & 0 deletions scripts/generateAdvancedAWS.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const {
applyAwsRegion,
applyAwsSecurityGroup,
applyAwsVpc,
applyAwsIamRole,
} = require('../dist/generators/addons/aws/modules/index.js');
const { applyAdvancedTemplate } = require('../dist/generators/addons/aws/advanced.js');

Expand All @@ -19,5 +20,6 @@ const { applyAdvancedTemplate } = require('../dist/generators/addons/aws/advance
await applyAwsVpc(options);
await applyAwsSecurityGroup(options);
await applyAwsIamUserAndGroup(options);
await applyAwsIamRole(options);
await applyAdvancedTemplate(options);
})();
7 changes: 7 additions & 0 deletions src/generators/addons/aws/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
applyAwsSecurityGroup,
applyAwsSsm,
applyAwsVpc,
applyAwsIamRole,
} from '@/generators/addons/aws/modules';
import { containsContent, isExisting } from '@/helpers/file';

Expand Down Expand Up @@ -85,6 +86,12 @@ const AWS_MODULES: Record<AwsModuleName | string, AwsModule> = {
mainContent: 'module "ssm"',
applyModuleFunction: (options: AwsOptions) => applyAwsSsm(options),
},
iamRole: {
name: 'iamRole',
path: 'modules/iam_role',
mainContent: 'module "iam_role"',
applyModuleFunction: (options: AwsOptions) => applyAwsIamRole(options),
},
};

const isAwsModuleAdded = (
Expand Down
2 changes: 2 additions & 0 deletions src/generators/addons/aws/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
applyAwsRegion,
applyAwsSecurityGroup,
applyAwsVpc,
applyAwsIamRole,
} from './modules';

const awsChoices = [
Expand Down Expand Up @@ -69,6 +70,7 @@ const generateAwsTemplate = async (
await applyAwsVpc(awsOptions);
await applyAwsSecurityGroup(awsOptions);
await applyAwsIamUserAndGroup(awsOptions);
await applyAwsIamRole(awsOptions);
await applyAdvancedTemplate(awsOptions);

break;
Expand Down
55 changes: 36 additions & 19 deletions src/generators/addons/aws/modules/bastion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
INFRA_CORE_LOCALS_PATH,
INFRA_CORE_MAIN_PATH,
INFRA_CORE_VARIABLES_PATH,
INFRA_CORE_DATA_PATH,
} from '@/generators/terraform/constants';
import { appendToFile, copy } from '@/helpers/file';

Expand All @@ -18,19 +19,23 @@ import {
AWS_TEMPLATE_PATH,
} from '../constants';

const bastionDataContent = dedent`
### Begin Bastion Host ###
data "aws_iam_policy" "ssm_managed_instance_core" {
name = "AmazonSSMManagedInstanceCore"
}
### End Bastion Host ###`;

const bastionLocalContent = dedent`
### Begin Bastion Host ###
locals {
enable_bastion = true
enable_bastion = true
bastion_ssm_role_name = "\${local.env_namespace}-SSMInstanceRole"
bastion_ssm_policy_arns = [data.aws_iam_policy.ssm_managed_instance_core.arn]
}
### End Bastion Host ###`;

const bastionVariablesContent = dedent`
variable "bastion_image_id" {
description = "The AMI image ID for the bastion instance"
default = "ami-0801a1e12f4a9ccc0"
}

variable "bastion_instance_type" {
description = "The bastion instance type"
default = "t3.nano"
Expand All @@ -52,16 +57,26 @@ const bastionVariablesContent = dedent`
}`;

const bastionModuleContent = dedent`
module "bastion_ssm_role" {
count = local.enable_bastion ? 1 : 0
source = "../modules/iam_role"

role_name = local.bastion_ssm_role_name
assume_role_services = ["ec2.amazonaws.com"]
policy_arns = local.bastion_ssm_policy_arns
create_instance_profile = true
}

module "bastion" {
count = local.enable_bastion ? 1 : 0
source = "../modules/bastion"

subnet_ids = module.vpc.public_subnet_ids
instance_security_group_ids = module.security_group.bastion_security_group_ids

env_namespace = local.env_namespace
image_id = var.bastion_image_id
instance_type = var.bastion_instance_type
env_namespace = local.env_namespace
instance_type = var.bastion_instance_type
iam_instance_profile = module.bastion_ssm_role[0].instance_profile_name

min_instance_count = var.bastion_min_instance_count
max_instance_count = var.bastion_max_instance_count
Expand All @@ -79,16 +94,6 @@ const bastionSGMainContent = dedent`
}
}

resource "aws_security_group_rule" "bastion_ingress_ssh_nimble" {
type = "ingress"
security_group_id = aws_security_group.bastion.id
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["\${var.nimble_office_ip}/32"]
description = "Nimble office"
}

resource "aws_security_group_rule" "bastion_egress_rds" {
type = "egress"
security_group_id = aws_security_group.bastion.id
Expand All @@ -97,6 +102,17 @@ const bastionSGMainContent = dedent`
protocol = "tcp"
source_security_group_id = aws_security_group.rds.id
description = "From RDS to bastion"
}

# trivy:ignore:AVD-AWS-0104
resource "aws_security_group_rule" "bastion_egress_ssm" {
type = "egress"
security_group_id = aws_security_group.bastion.id
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow outbound HTTPS traffic for SSM"
}`;

const bastionSGOutputsContent = dedent`
Expand All @@ -121,6 +137,7 @@ const applyAwsBastion = async (options: AwsOptions) => {
bastionLocalContent,
options.projectName
);
appendToFile(INFRA_CORE_DATA_PATH, bastionDataContent, options.projectName);
appendToFile(
INFRA_CORE_VARIABLES_PATH,
bastionVariablesContent,
Expand Down
45 changes: 45 additions & 0 deletions src/generators/addons/aws/modules/core/iamRole.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { AwsOptions } from '@/generators/addons/aws';
import { applyTerraformCore } from '@/generators/terraform';
import { remove } from '@/helpers/file';

import applyAwsIamRole from './iamRole';
import applyTerraformAwsProvider from './provider';

describe('IAM Role add-on', () => {
describe('given valid AWS options', () => {
const projectDir = 'iam-addon-test';

beforeAll(async () => {
const awsOptions: AwsOptions = {
projectName: projectDir,
provider: 'aws',
infrastructureType: 'advanced',
};

await applyTerraformCore(awsOptions);
await applyTerraformAwsProvider(awsOptions);
await applyAwsIamRole(awsOptions);
});

afterAll(() => {
jest.clearAllMocks();
remove('/', projectDir);
});

it('creates expected files', () => {
const expectedFiles = [
'shared/main.tf',
'shared/providers.tf',
'shared/outputs.tf',
'shared/variables.tf',

'modules/iam_role/data.tf',
'modules/iam_role/variables.tf',
'modules/iam_role/main.tf',
'modules/iam_role/outputs.tf',
];

expect(projectDir).toHaveFiles(expectedFiles);
});
});
});
18 changes: 18 additions & 0 deletions src/generators/addons/aws/modules/core/iamRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AwsOptions } from '@/generators/addons/aws';
import { AWS_TEMPLATE_PATH } from '@/generators/addons/aws/constants';
import { isAwsModuleAdded } from '@/generators/addons/aws/dependencies';
import { copy } from '@/helpers/file';

const applyAwsIamRole = async (options: AwsOptions) => {
if (isAwsModuleAdded('iamRole', options.projectName)) {
return;
}

copy(
`${AWS_TEMPLATE_PATH}/modules/iam_role`,
'modules/iam_role',
options.projectName
);
};

export default applyAwsIamRole;
2 changes: 2 additions & 0 deletions src/generators/addons/aws/modules/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import applyAwsAlb from './alb';
import applyAwsBastion from './bastion';
import applyAwsCloudwatch from './cloudwatch';
import applyAwsIamRole from './core/iamRole';
import applyAwsIamUserAndGroup from './core/iamUserAndGroup';
import applyTerraformAwsProvider from './core/provider';
import applyAwsRegion from './core/region';
Expand All @@ -14,6 +15,7 @@ import applyAwsSsm from './ssm';

export {
applyAwsAlb,
applyAwsIamRole,
applyAwsBastion,
applyTerraformAwsProvider,
applyAwsCloudwatch,
Expand Down
19 changes: 19 additions & 0 deletions templates/addons/aws/modules/bastion/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
data "aws_ami" "amazon_linux_2023" {
most_recent = true
owners = ["amazon"]

filter {
name = "name"
values = ["al2023-ami-2023.*-x86_64"]
}

filter {
name = "architecture"
values = ["x86_64"]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}
}
7 changes: 5 additions & 2 deletions templates/addons/aws/modules/bastion/main.tf
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# trivy:ignore:AVD-AWS-0009
resource "aws_launch_template" "bastion_instance" {
name_prefix = "${local.name_prefix}-"
image_id = var.image_id
image_id = data.aws_ami.amazon_linux_2023.id
instance_type = var.instance_type
key_name = var.key_name

iam_instance_profile {
name = var.iam_instance_profile
}

metadata_options {
http_tokens = local.metadata_options.http_tokens
Expand Down
13 changes: 4 additions & 9 deletions templates/addons/aws/modules/bastion/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ variable "env_namespace" {
}

variable "subnet_ids" {
description = "The public setnet IsD for the instance"
description = "The public subnet IDs for the instance"
type = list(string)
}

Expand All @@ -13,11 +13,6 @@ variable "instance_security_group_ids" {
type = list(string)
}

variable "image_id" {
description = "The AMI image ID"
default = "ami-0801a1e12f4a9ccc0"
}

variable "instance_type" {
description = "The instance type"
default = "t3.nano"
Expand Down Expand Up @@ -47,11 +42,11 @@ variable "volume_size" {
variable "device_name" {
description = "The device name for the EBS volume"
type = string
default = "/dev/sdf"
default = "/dev/xvda"
}

variable "key_name" {
description = "The name of the key pair to use for the instance"
variable "iam_instance_profile" {
description = "The name of the IAM instance profile for the instance"
type = string
default = ""
}
12 changes: 12 additions & 0 deletions templates/addons/aws/modules/iam_role/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"

principals {
type = "Service"
identifiers = var.assume_role_services
}

actions = ["sts:AssumeRole"]
}
}
26 changes: 26 additions & 0 deletions templates/addons/aws/modules/iam_role/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
resource "aws_iam_role" "role" {
name = var.role_name
assume_role_policy = data.aws_iam_policy_document.assume_role.json

tags = {
Name = var.role_name
}
}

resource "aws_iam_role_policy_attachment" "this" {
for_each = toset(var.policy_arns)

role = aws_iam_role.role.name
policy_arn = each.value
}

resource "aws_iam_instance_profile" "instance_profile" {
count = var.create_instance_profile ? 1 : 0

name = var.role_name
role = aws_iam_role.role.name

tags = {
Name = var.role_name
}
}
9 changes: 9 additions & 0 deletions templates/addons/aws/modules/iam_role/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "role_arn" {
description = "The ARN of the IAM role"
value = aws_iam_role.role.arn
}

output "instance_profile_name" {
description = "The name of the IAM instance profile"
value = var.create_instance_profile ? aws_iam_instance_profile.instance_profile[0].name : null
}
22 changes: 22 additions & 0 deletions templates/addons/aws/modules/iam_role/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
variable "role_name" {
description = "The name of the IAM role"
type = string
}

variable "assume_role_services" {
description = "List of AWS services that can assume this role"
type = list(string)
default = ["ec2.amazonaws.com"]
}

variable "policy_arns" {
description = "List of IAM policy ARNs to attach to the role"
type = list(string)
default = []
}

variable "create_instance_profile" {
description = "Whether to create an IAM instance profile"
type = bool
default = false
}
2 changes: 1 addition & 1 deletion templates/terraform/.tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
terraform 1.13.3
trivy 0.47.0
trivy 0.69.2