NEW: modules for creating deployer roles and using deployer roles

This commit is contained in:
xpk 2023-09-29 12:30:10 +08:00
parent 185036deeb
commit 431d2e0bd9
Signed by: xpk
GPG Key ID: CD4FF6793F09AB86
16 changed files with 1515 additions and 0 deletions

View File

@ -0,0 +1,275 @@
# vpc-endpoints module
This module deploys VPC endpoints.
Automatically, this module performs the following additional tasks
- Create and attach security group which allows access from the same VPC
- Associate endpoints with 1 subnet in each availability zone
# Inputs
| Variable | Type | Required | Description |
|-----------------------|--------------|----------|-------------------------------------------------|
| voc-id | string | yes | ID of VPC to deploy endpoints to |
| interface-ep-services | list(string) | yes | Interface endpoint names |
| gateway-ep-services | list(string) | no | Gateway endpoint names |
| resource-prefix | string | yes | Prefix that will be added to resource name tags |
# Types of endpoints
## Gateway endpoints
At time of writing, AWS provides 2 gateway endpoints at no charge.
* s3
* dynamodb
For gateway endpoints, all route tables in the VPC will be updated with routes to the private links.
Full documentation: https://docs.aws.amazon.com/vpc/latest/privatelink/gateway-endpoints.html
## Interface endpoints
Interface endpoints are placed in one subnet for every AZ. Security group is created automatically
and allow access from the VPC's cidr, plus all additional CIDRs if applicable.
At time of writing, AWS provides 200+ interface endpoints:
* access-analyzer
* account
* execute-api
* appmesh
* appmesh-envoy-management
* apprunner
* apprunner.requests
* application-autoscaling
* mgn
* appstream.api
* appstream.streaming
* appsync-api
* athena
* auditmanager
* rds
* autoscaling-plans
* backup
* backup-gateway
* batch
* billingconductor
* braket
* cleanrooms
* cloudcontrolapi
* cloudcontrolapi-fips
* clouddirectory
* cloudformation
* cloudhsmv2
* cloudtrail
* evidently
* evidently-dataplane
* monitoring
* rum
* rum-dataplane
* synthetics
* events
* logs
* codeartifact.api
* codeartifact.repositories
* codebuild
* codebuild-fips
* codecommit
* codecommit-fips
* git-codecommit
* git-codecommit-fips
* codedeploy
* codedeploy-commands-secure
* codeguru-profiler
* codeguru-reviewer
* codepipeline
* codestar-connections.api
* comprehend
* comprehendmedical
* config
* app-integrations
* cases
* connect-campaigns
* profile
* voiceid
* wisdom
* dataexchange
* dms
* dms-fips
* datasync
* devops-guru
* ds
* ebs
* ec2
* autoscaling
* imagebuilder
* ecr.api
* ecr.dkr
* ecs
* ecs-agent
* ecs-telemetry
* eks
* elasticbeanstalk
* elasticbeanstalk-health
* drs
* elasticfilesystem
* elasticfilesystem-fips
* elastic-inference.runtime
* elasticloadbalancing
* elasticache
* elasticache-fips
* elasticmapreduce
* emr-containers
* emr-serverless
* events
* fis
* finspace
* finspace-api
* forecast
* forecastquery
* forecast-fips
* forecastquery-fips
* frauddetector
* fsx
* fsx-fips
* glue
* databrew
* grafana
* grafana-workspace
* groundstation
* guardduty-data
* guardduty-data-fips
* healthlake
* identitystore
* rolesanywhere
* inspector2
* iot.data
* iot.fleethub.api
* deviceadvisor.iot
* iotwireless.api
* lorawan.cups
* lorawan.lns
* iotfleetwise
* greengrass
* iotroborunner
* iotsitewise.api
* iotsitewise.data
* iottwinmaker.api
* iottwinmaker.data
* kendra
* kendra-ranking
* kms
* kms-fips
* cassandra
* cassandra-fips
* kinesis-firehose
* kinesis-streams
* lakeformation
* lambda
* models-v2-lex
* runtime-v2-lex
* license-manager
* license-manager-fips
* lookoutequipment
* lookoutmetrics
* lookoutvision
* macie2
* m2
* aps
* aps-workspaces
* airflow.api
* airflow.env
* airflow.ops
* console
* signin
* memory-db
* memorydb-fips
* migrationhub-orchestrator
* refactor-spaces
* migrationhub-strategy
* nimble
* analytics-omics
* control-storage-omics
* storage-omics
* tags-omics
* workflows-omics
* service-managed
* panorama
* payment-cryptography.controlplane
* payment-cryptography.dataplane
* personalize
* personalize-events
* personalize-runtime
* pinpoint
* pinpoint-sms-voice-v2
* polly
* private-networks
* acm-pca
* proton
* qldb.session
* rds
* rds-data
* redshift
* redshift-fips
* redshift-data
* rekognition
* rekognition-fips
* streaming-rekognition
* streaming-rekognition-fips
* robomaker
* s3
* com.amazonaws.s3-global.accesspoint
* s3-outposts
* aws.sagemaker.region.notebook
* aws.sagemaker.region.studio
* sagemaker.api
* sagemaker.featurestore-runtime
* sagemaker.metrics
* sagemaker.runtime
* sagemaker.runtime-fips
* secretsmanager
* securityhub
* sts
* servicecatalog
* servicecatalog-appregistry
* email-smtp
* simspaceweaver
* snow-device-management
* sns
* sqs
* swf
* swf-fips
* states
* sync-states
* storagegateway
* ec2messages
* ssm
* ssm-contacts
* ssm-incidents
* ssmmessages
* tnb
* textract
* textract-fips
* transcribe
* transcribestreaming
* transcribe
* transcribestreaming
* transfer
* transfer.server
* translate
* verifiedpermissions
* vpc-lattice
* workspaces
* xray
Full documentation: https://docs.aws.amazon.com/vpc/latest/privatelink/aws-services-privatelink-support.html
## Example
```hcl
module "vpc-ep" {
count = var.create-free-vpc-endpoints ? 1 : 0
source = "../vpc-endpoints"
gateway-ep-services = ["s3", "dynamodb"]
interface-ep-services = []
resource-prefix = var.resource-prefix
vpc-id = aws_vpc.vpc.id
}
```

View File

@ -0,0 +1,114 @@
data "aws_region" "this" {
provider = aws.NetworkDeployer
}
data "aws_default_tags" "this" {
lifecycle {
postcondition {
condition = length(self.tags) >= 1
error_message = "Validation failed: Provider default_tags not set."
}
}
}
resource "aws_vpc_endpoint" "vpc-interface-ep" {
provider = aws.NetworkDeployer
for_each = toset(var.interface-ep-services)
vpc_id = data.aws_vpc.this-vpc.id
service_name = "com.amazonaws.${data.aws_region.this.name}.${each.value}"
vpc_endpoint_type = "Interface"
security_group_ids = [
aws_security_group.vpc-ep-sg.id,
]
# deploy to all subnets
subnet_ids = local.one_subnet_in_each_az
private_dns_enabled = true
tags = { "Name" : "${var.resource-prefix}-vpcep-${each.value}" }
lifecycle {
precondition {
condition = data.aws_vpc.this-vpc.enable_dns_support
error_message = "enableDnsSupport needs to be turned on."
}
}
}
resource "aws_vpc_endpoint" "vpc-gateway-ep" {
provider = aws.NetworkDeployer
for_each = toset(var.gateway-ep-services)
vpc_id = data.aws_vpc.this-vpc.id
service_name = "com.amazonaws.${data.aws_region.this.name}.${each.value}"
vpc_endpoint_type = "Gateway"
route_table_ids = data.aws_route_tables.this.ids
tags = { "Name" : "${var.resource-prefix}-vpcep-${each.value}" }
}
resource "random_id" "rid" {
byte_length = 2
}
resource "aws_security_group" "vpc-ep-sg" {
provider = aws.NetworkDeployer
name = "HttpsAccessToVpcEndpoints-${random_id.rid.dec}"
description = "HttpsAccessToVpcEndpoints-${random_id.rid.dec}"
vpc_id = data.aws_vpc.this-vpc.id
ingress {
description = "TLS from VPC"
from_port = 443
to_port = 443
protocol = "tcp"
# cidr_blocks = [data.aws_vpc.this-vpc.cidr_block]
cidr_blocks = data.aws_vpc.this-vpc.cidr_block_associations.*.cidr_block
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = { "Name" : "VpcEpAccess" }
}
data "aws_vpc" "this-vpc" {
provider = aws.NetworkDeployer
id = var.vpc-id
}
data "aws_availability_zones" "this" {
provider = aws.NetworkDeployer
state = "available"
}
# find all subnets for this vpc in all availability zones
data "aws_subnets" "subnets_and_az" {
provider = aws.NetworkDeployer
for_each = toset(data.aws_availability_zones.this.zone_ids)
filter {
name = "vpc-id"
values = [var.vpc-id]
}
filter {
name = "availability-zone-id"
values = [each.value]
}
}
data "aws_route_tables" "this" {
vpc_id = var.vpc-id
}
locals {
# pick first subnet in each AZ
one_subnet_in_each_az = compact([for k, v in data.aws_subnets.subnets_and_az : try(element(v.ids, length(v.ids) - 1), "")])
}

View File

@ -0,0 +1,12 @@
# requires 1.3.0 for postcondition validation
# https://learn.hashicorp.com/tutorials/terraform/custom-conditions
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 3.75.2"
configuration_aliases = [ aws.NetworkDeployer, aws.SecurityDeployer, aws.CommonDeployer ]
}
}
}

View File

@ -0,0 +1,18 @@
variable vpc-id {}
variable interface-ep-services {
type = list(string)
description = "List of interface endpoint. E.g. dkr,lambda,kms,elasticloadbalancing,execute-api,ec2,ssm,secretsmanager,monitoring,guardduty-data"
}
variable gateway-ep-services {
type = list(string)
default = []
description = "s3 and dynamodb gateway endpoints are free."
}
variable resource-prefix {}
/*
variable secondary_cidrs {
type = list(string)
description = "Additional cidr blocks"
default = []
}
*/

View File

@ -0,0 +1,52 @@
# Overview
This module performs the following tasks:
- Create VPC, vpcflow log
- Create subnets in every AZ
- Create IGW, NGW
- Create s3 and ddb endpoints which are free
## Subnet addressing
Subnet cidrs needs to be specified manually
## Inputs:
| Name | Description | Type | Default | Required |
|---------------------------------|---------------------------------------------------|---------------|---------|----------|
| private-subnet-cidrs | private subnets | list | [] | yes |
| public-subnet-cidrs | public subnets | list | [] | yes |
| create-nat-gateway | whether to deploy NAT gateway for private subnets | bool | true | yes |
| vpc-cidr | VPC cidr | string | none | yes |
| enable-flowlog | whether to enable vpc flowlog | bool | true | yes |
| vpcflowlog-retain-days | number of days to retain vpc cloudwatch log | number | 90 | yes |
| vpcflowlog-cwl-loggroup-key-arn | kms key alias arn for log group encryption | string | none | yes |
| secondary_cidr_blocks | Additional CIDR blocks to be associated with VPC | list(string) | none | no |
| resource-prefix | Prefix of resource name | string | "" | yes |
## Outputs:
| Name | Description | Type |
|-----------------------|-------------------------|---------|
| vpc_id | vpc id | string |
| public_subnets | list of cidr blocks | list |
| private_subnets | list of cidr blocks | list |
| secondary_cidr_blocks | list of secondary cidrs | list |
## Example:
```hcl
module "vpc-subnets" {
source = "../../modules/networking/vpc-subnet-manual"
resource-prefix = local.resource-prefix
private-subnet-cidrs = ["172.17.0.0/24", "172.17.1.0/24"]
public-subnet-cidrs = ["172.17.10.0/24", "172.17.11.0/24"]
vpc-cidr = "172.17.0.0/16"
enable-flow-log = false
vpcflowlog-cwl-loggroup-key-arn = ""
create-nat-gateway = true
create-free-vpc-endpoints = true
}
```

View File

@ -0,0 +1,206 @@
data "aws_caller_identity" "this" {
provider = aws.NetworkDeployer
}
data "aws_availability_zones" "available-az" {
provider = aws.NetworkDeployer
state = "available"
}
data "aws_default_tags" "this" {
lifecycle {
postcondition {
condition = length(self.tags) >= 1
error_message = "Validation failed: Provider default_tags not set."
}
}
}
locals {
no-az = 2 # hard-coding to 2AZ
vpc-cidr = var.vpc-cidr
}
resource "aws_subnet" "private-subnets" {
provider = aws.NetworkDeployer
count = length(var.private-subnet-cidrs)
vpc_id = aws_vpc.vpc.id
availability_zone = element(data.aws_availability_zones.available-az.names, count.index % 2)
cidr_block = var.private-subnet-cidrs[count.index]
tags = merge(data.aws_default_tags.this.tags, {
Name = "${var.resource-prefix}-private-${split("-", element(data.aws_availability_zones.available-az.names, count.index))[2]}-${count.index + 1}"
})
}
resource "aws_subnet" "public-subnets" {
provider = aws.NetworkDeployer
count = length(var.public-subnet-cidrs)
vpc_id = aws_vpc.vpc.id
availability_zone = element(data.aws_availability_zones.available-az.names, count.index % 2)
cidr_block = var.public-subnet-cidrs[count.index]
tags = merge(data.aws_default_tags.this.tags, {
Name = "${var.resource-prefix}-public-${split("-", element(data.aws_availability_zones.available-az.names, count.index))[2]}-${count.index + 1}"
})
}
resource "aws_vpc" "vpc" {
provider = aws.NetworkDeployer
cidr_block = var.vpc-cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.resource-prefix}-vpc"
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_vpc_ipv4_cidr_block_association" "additional_cidr" {
provider = aws.NetworkDeployer
for_each = toset(var.secondary_cidr_blocks)
vpc_id = aws_vpc.vpc.id
cidr_block = each.value
}
resource "aws_internet_gateway" "igw" {
provider = aws.NetworkDeployer
count = length(var.public-subnet-cidrs) > 0 ? 1 : 0
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.resource-prefix}-igw"
}
}
resource "aws_eip" "ngw-eip" {
provider = aws.NetworkDeployer
count = var.create-nat-gateway ? 1 : 0
# deprecated # vpc = true
domain = "vpc"
depends_on = [aws_internet_gateway.igw]
}
resource "aws_nat_gateway" "ngw" {
provider = aws.NetworkDeployer
count = var.create-nat-gateway ? 1 : 0
allocation_id = aws_eip.ngw-eip[0].id
subnet_id = aws_subnet.public-subnets[0].id
tags = {
Name = "${var.resource-prefix}-ngw"
}
depends_on = [aws_internet_gateway.igw]
}
resource "aws_route_table" "public-route-table" {
provider = aws.NetworkDeployer
count = length(var.public-subnet-cidrs) > 0 ? 1 : 0
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.resource-prefix}-publicroutetable"
}
}
resource "aws_route_table" "private-route-table" {
provider = aws.NetworkDeployer
count = length(var.private-subnet-cidrs) > 0 ? 1 : 0
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.resource-prefix}-privateroutetable"
}
}
resource "aws_route" "public-routes" {
provider = aws.NetworkDeployer
count = length(var.public-subnet-cidrs) > 0 ? 1 : 0
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw[0].id
route_table_id = aws_route_table.public-route-table[0].id
}
resource "aws_route" "private-routes" {
provider = aws.NetworkDeployer
count = length(var.private-subnet-cidrs) > 0 && var.create-nat-gateway ? 1 : 0
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.ngw[0].id
route_table_id = aws_route_table.private-route-table[0].id
}
resource "aws_route_table_association" "public_route_association" {
provider = aws.NetworkDeployer
count = length(aws_subnet.public-subnets)
route_table_id = aws_route_table.public-route-table[0].id
subnet_id = aws_subnet.public-subnets[count.index].id
}
resource "aws_route_table_association" "private_route_association" {
provider = aws.NetworkDeployer
count = length(aws_subnet.private-subnets)
route_table_id = aws_route_table.private-route-table[0].id
subnet_id = aws_subnet.private-subnets[count.index].id
}
/*
harden default security group. the default sg created by aws allows all egress.
this resource limits ingress and egress from and to itself
*/
resource "aws_default_security_group" "default-sg" {
provider = aws.NetworkDeployer
vpc_id = aws_vpc.vpc.id
ingress {
protocol = -1
self = true
from_port = 0
to_port = 0
description = "Allow traffic coming from this SG"
}
egress {
from_port = 0
protocol = -1
to_port = 0
self = true
description = "Allow traffic going to this SG"
}
tags = {
Name = "${var.resource-prefix}-defaultsg"
}
}
# Enable gateway endpoints which are free
module "vpc-ep" {
providers = {
aws.NetworkDeployer = aws.NetworkDeployer
aws.CommonDeployer = aws.CommonDeployer
aws.SecurityDeployer = aws.SecurityDeployer
}
count = var.create-free-vpc-endpoints ? 1 : 0
source = "../vpc-endpoints-5r"
gateway-ep-services = ["s3", "dynamodb"]
interface-ep-services = []
resource-prefix = var.resource-prefix
vpc-id = aws_vpc.vpc.id
}

View File

@ -0,0 +1,39 @@
output "vpc_id" {
value = aws_vpc.vpc.id
}
output "vpc-cidr" {
value = aws_vpc.vpc.cidr_block
}
output "public_subnets" {
value = aws_subnet.public-subnets.*.cidr_block
}
output "private_subnets" {
value = aws_subnet.private-subnets.*.cidr_block
}
output "public-subnet-ids" {
value = aws_subnet.public-subnets.*.id
}
output "private-subnet-ids" {
value = aws_subnet.private-subnets.*.id
}
output "private-route-table-id" {
value = aws_route_table.private-route-table.*.id
}
output "public-route-table-id" {
value = aws_route_table.public-route-table.*.id
}
output "route_tables_for_gateway_endpoints" {
value = concat(aws_route_table.public-route-table.*.id, aws_route_table.private-route-table.*.id)
}
output "secondary_cidr_blocks" {
value = var.secondary_cidr_blocks
}

View File

@ -0,0 +1,15 @@
# requires 1.3.0 for postcondition validation
# https://learn.hashicorp.com/tutorials/terraform/custom-conditions
# provider 5.0.0 or above is required by the domain attribute in aws_eip
terraform {
required_version = "~> 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0.0"
configuration_aliases = [ aws.NetworkDeployer, aws.SecurityDeployer, aws.CommonDeployer ]
}
}
}

View File

@ -0,0 +1,37 @@
variable resource-prefix {}
# VPC variables
variable "vpc-cidr" {}
variable "private-subnet-cidrs" {
type = list(any)
}
variable "public-subnet-cidrs" {
type = list(any)
}
variable "create-nat-gateway" {
type = bool
default = false
}
variable "enable-flow-log" {
type = bool
default = true
}
variable "vpcflowlog-retain-days" {
type = number
default = 90
}
variable "vpcflowlog-cwl-loggroup-key-arn" {}
# variable "private-subnet-cidrs" {}
# variable "public-subnet-cidrs" {}
variable "create-free-vpc-endpoints" {
type = bool
default = true
}
variable "secondary_cidr_blocks" {
type = list(string)
description = "Additional cidr blocks"
default = []
}

View File

@ -0,0 +1,71 @@
resource "aws_flow_log" "vpc-flowlog" {
provider = aws.NetworkDeployer
count = var.enable-flow-log ? 1 : 0
iam_role_arn = aws_iam_role.vpcflowlog-role.arn
log_destination = aws_cloudwatch_log_group.vpcflowlog-loggroup[0].arn
traffic_type = "ALL"
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.resource-prefix}-vpcflowlog"
}
}
resource "aws_cloudwatch_log_group" "vpcflowlog-loggroup" {
provider = aws.CommonDeployer
count = var.enable-flow-log ? 1 : 0
name_prefix = "vpcflowlog/${aws_vpc.vpc.id}/"
kms_key_id = var.vpcflowlog-cwl-loggroup-key-arn
retention_in_days = var.vpcflowlog-retain-days
}
resource "random_id" "rid" {
byte_length = 2
}
resource "aws_iam_role" "vpcflowlog-role" {
provider = aws.SecurityDeployer
name = "VpcFlowlogRole-${random_id.rid.dec}"
path = "/service/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "vpc-flow-logs.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "vpcflowlog-role-policy" {
provider = aws.SecurityDeployer
name = "VpcFlowlogRole-${random_id.rid.dec}"
role = aws_iam_role.vpcflowlog-role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}

View File

@ -0,0 +1,6 @@
# Deployer roles
The roles are created based on the design in *CICD High Level Architecture-RevisedWithRoles.pptx*
# Changelog
20230313: Initial release
20230929: Added iam:PassRole to NetworkDeployer for creating vpc flowlogs

View File

@ -0,0 +1 @@
data "aws_caller_identity" "this" {}

View File

@ -0,0 +1,639 @@
data "aws_default_tags" "this" {
lifecycle {
postcondition {
condition = length(self.tags) >= 1
error_message = "Validation failed: Provider default_tags not set."
}
}
}
data "aws_iam_policy_document" "assume-role-policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = [var.role-trusted-entity-arn]
}
}
}
resource "aws_iam_role" "SecurityDeployer" {
name = "SecurityDeployer"
description = "Admin access to IAM, KMS, SecretsManager, ec2 Key Pair"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
resource "aws_iam_role_policy" "SecurityDeployerPolicy" {
name = "SecurityDeployerPolicy"
policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"iam:*",
"secretsmanager:*",
"ec2:ImportKeyPair",
"kms:*",
"ec2:CreateKeyPair",
"ec2:DescribeKeyPairs",
"ec2:DeleteKeyPair",
"acm:*",
"config:*",
"guardduty:*",
"inspector2:*",
"securityhub:*",
"shield:*",
"sso:*",
"organizations:*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.SecurityDeployer.id
}
resource "aws_iam_role" "NetworkDeployer" {
name = "NetworkDeployer"
description = "Admin access to VPC, SecurityGroup, Route53"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
# iam:PassRole required to create flowlogs
resource "aws_iam_role_policy" "NetworkDeployerPolicy" {
name = "NetworkDeployerPolicy"
policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"iam:PassRole",
"ec2:AcceptVpcEndpointConnections",
"ec2:AllocateAddress",
"ec2:AssignIpv6Addresses",
"ec2:AssignPrivateIpAddresses",
"ec2:AssociateAddress",
"ec2:AssociateDhcpOptions",
"ec2:AssociateRouteTable",
"ec2:AssociateSubnetCidrBlock",
"ec2:AssociateVpcCidrBlock",
"ec2:AttachInternetGateway",
"ec2:AttachNetworkInterface",
"ec2:AttachVpnGateway",
"ec2:CreateCarrierGateway",
"ec2:CreateCustomerGateway",
"ec2:CreateDefaultSubnet",
"ec2:CreateDefaultVpc",
"ec2:CreateDhcpOptions",
"ec2:CreateEgressOnlyInternetGateway",
"ec2:CreateFlowLogs",
"ec2:CreateInternetGateway",
"ec2:CreateNatGateway",
"ec2:CreateNetworkAcl",
"ec2:CreateNetworkAclEntry",
"ec2:CreateNetworkInterface",
"ec2:CreateNetworkInterfacePermission",
"ec2:CreatePlacementGroup",
"ec2:CreateRoute",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVpc",
"ec2:CreateVpcEndpoint",
"ec2:CreateVpcEndpointConnectionNotification",
"ec2:CreateVpcEndpointServiceConfiguration",
"ec2:CreateVpnConnection",
"ec2:CreateVpnConnectionRoute",
"ec2:CreateVpnGateway",
"ec2:DeleteCarrierGateway",
"ec2:DeleteEgressOnlyInternetGateway",
"ec2:DeleteFlowLogs",
"ec2:DeleteNatGateway",
"ec2:DeleteNetworkInterface",
"ec2:DeleteNetworkInterfacePermission",
"ec2:DeletePlacementGroup",
"ec2:DeleteSubnet",
"ec2:DeleteTags",
"ec2:DeleteVpc",
"ec2:DeleteVpcEndpointConnectionNotifications",
"ec2:DeleteVpcEndpointServiceConfigurations",
"ec2:DeleteVpcEndpoints",
"ec2:DeleteVpnConnection",
"ec2:DeleteVpnConnectionRoute",
"ec2:DeleteVpnGateway",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeCarrierGateways",
"ec2:DescribeClassicLinkInstances",
"ec2:DescribeCustomerGateways",
"ec2:DescribeDhcpOptions",
"ec2:DescribeEgressOnlyInternetGateways",
"ec2:DescribeFlowLogs",
"ec2:DescribeInstances",
"ec2:DescribeInternetGateways",
"ec2:DescribeKeyPairs",
"ec2:DescribeMovingAddresses",
"ec2:DescribeNatGateways",
"ec2:DescribeNetworkAcls",
"ec2:DescribeNetworkInterfaceAttribute",
"ec2:DescribeNetworkInterfacePermissions",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribePlacementGroups",
"ec2:DescribePrefixLists",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupReferences",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeStaleSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcClassicLink",
"ec2:DescribeVpcClassicLinkDnsSupport",
"ec2:DescribeVpcEndpointConnectionNotifications",
"ec2:DescribeVpcEndpointConnections",
"ec2:DescribeVpcEndpointServiceConfigurations",
"ec2:DescribeVpcEndpointServicePermissions",
"ec2:DescribeVpcEndpointServices",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeVpcs",
"ec2:DescribeVpnConnections",
"ec2:DescribeVpnGateways",
"ec2:DescribePublicIpv4Pools",
"ec2:DescribeIpv6Pools",
"ec2:DetachInternetGateway",
"ec2:DetachNetworkInterface",
"ec2:DetachVpnGateway",
"ec2:DisableVgwRoutePropagation",
"ec2:DisableVpcClassicLinkDnsSupport",
"ec2:DisassociateAddress",
"ec2:DisassociateRouteTable",
"ec2:DisassociateSubnetCidrBlock",
"ec2:DisassociateVpcCidrBlock",
"ec2:EnableVgwRoutePropagation",
"ec2:EnableVpcClassicLinkDnsSupport",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:ModifySecurityGroupRules",
"ec2:ModifySubnetAttribute",
"ec2:ModifyVpcAttribute",
"ec2:ModifyVpcEndpoint",
"ec2:ModifyVpcEndpointConnectionNotification",
"ec2:ModifyVpcEndpointServiceConfiguration",
"ec2:ModifyVpcEndpointServicePermissions",
"ec2:ModifyVpcPeeringConnectionOptions",
"ec2:ModifyVpcTenancy",
"ec2:MoveAddressToVpc",
"ec2:RejectVpcEndpointConnections",
"ec2:ReleaseAddress",
"ec2:ReplaceNetworkAclAssociation",
"ec2:ReplaceNetworkAclEntry",
"ec2:ReplaceRoute",
"ec2:ReplaceRouteTableAssociation",
"ec2:ResetNetworkInterfaceAttribute",
"ec2:RestoreAddressToClassic",
"ec2:UnassignIpv6Addresses",
"ec2:UnassignPrivateIpAddresses",
"ec2:UpdateSecurityGroupRuleDescriptionsEgress",
"ec2:UpdateSecurityGroupRuleDescriptionsIngress",
"ec2:AcceptVpcPeeringConnection",
"ec2:AttachClassicLinkVpc",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateVpcPeeringConnection",
"ec2:DeleteCustomerGateway",
"ec2:DeleteDhcpOptions",
"ec2:DeleteInternetGateway",
"ec2:DeleteNetworkAcl",
"ec2:DeleteNetworkAclEntry",
"ec2:DeleteRoute",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteVolume",
"ec2:DeleteVpcPeeringConnection",
"ec2:DetachClassicLinkVpc",
"ec2:DisableVpcClassicLink",
"ec2:EnableVpcClassicLink",
"ec2:GetConsoleScreenshot",
"ec2:RejectVpcPeeringConnection",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:CreateLocalGatewayRoute",
"ec2:CreateLocalGatewayRouteTableVpcAssociation",
"ec2:DeleteLocalGatewayRoute",
"ec2:DeleteLocalGatewayRouteTableVpcAssociation",
"ec2:DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations",
"ec2:DescribeLocalGatewayRouteTableVpcAssociations",
"ec2:DescribeLocalGatewayRouteTables",
"ec2:DescribeLocalGatewayVirtualInterfaceGroups",
"ec2:DescribeLocalGatewayVirtualInterfaces",
"ec2:DescribeLocalGateways",
"ec2:SearchLocalGatewayRoutes",
"ec2:AcceptTransitGatewayVpcAttachment",
"ec2:AssociateTransitGatewayRouteTable",
"ec2:CreateTransitGateway",
"ec2:CreateTransitGatewayRoute",
"ec2:CreateTransitGatewayRouteTable",
"ec2:CreateTransitGatewayVpcAttachment",
"ec2:DeleteTransitGateway",
"ec2:DeleteTransitGatewayRoute",
"ec2:DeleteTransitGatewayRouteTable",
"ec2:DeleteTransitGatewayVpcAttachment",
"ec2:DescribeTransitGatewayAttachments",
"ec2:DescribeTransitGatewayRouteTables",
"ec2:DescribeTransitGatewayVpcAttachments",
"ec2:DescribeTransitGateways",
"ec2:DisableTransitGatewayRouteTablePropagation",
"ec2:DisassociateTransitGatewayRouteTable",
"ec2:EnableTransitGatewayRouteTablePropagation",
"ec2:ExportTransitGatewayRoutes",
"ec2:GetTransitGatewayAttachmentPropagations",
"ec2:GetTransitGatewayRouteTableAssociations",
"ec2:GetTransitGatewayRouteTablePropagations",
"ec2:ModifyTransitGateway",
"ec2:ModifyTransitGatewayVpcAttachment",
"ec2:RejectTransitGatewayVpcAttachment",
"ec2:ReplaceTransitGatewayRoute",
"ec2:SearchTransitGatewayRoutes",
"route53domains:*",
"route53resolver:*",
"route53:*",
"directconnect:*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.NetworkDeployer.id
}
resource "aws_iam_role" "DatabaseDeployer" {
name = "DatabaseDeployer"
description = "Admin access to databases"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
resource "aws_iam_role_policy" "DatabaseDeployerPolicy" {
name = "DatabaseDeployerPolicy"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"rds:*",
"redshift:*",
"elasticache:*",
"kms:Get*",
"kms:List*",
"kms:Describe*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.DatabaseDeployer.id
}
resource "aws_iam_role" "StorageDeployer" {
name = "StorageDeployer"
description = "Admin access to S3, RDS, ElastiCache, ECR"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
resource "aws_iam_role_policy" "StorageDeployerPolicy" {
name = "StorageDeployerPolicy"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"s3:*",
"ecr:*",
"elasticfilesystem:*",
"fsx:*",
"kms:Get*",
"kms:List*",
"kms:Describe*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.StorageDeployer.id
}
resource "aws_iam_role" "CommonDeployer" {
name = "CommonDeployer"
description = "Admin access to all services except those allowed in other deployer roles"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
resource "aws_iam_role_policy" "CommonDeployerPolicy" {
name = "CommonDeployerPolicy"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "NegateSecurityDeployerPermissions",
"Effect" : "Allow",
"NotAction" : [
"iam:*",
"secretsmanager:*",
"ec2:ImportKeyPair",
"kms:EnableKey",
"kms:ImportKeyMaterial",
"kms:Decrypt",
"kms:GenerateRandom",
"kms:PutKeyPolicy",
"kms:GenerateDataKeyWithoutPlaintext",
"kms:Verify",
"kms:CancelKeyDeletion",
"kms:ReplicateKey",
"kms:GenerateDataKeyPair",
"kms:SynchronizeMultiRegionKey",
"kms:DeleteCustomKeyStore",
"kms:GenerateMac",
"kms:UpdatePrimaryRegion",
"kms:UpdateCustomKeyStore",
"kms:Encrypt",
"kms:ScheduleKeyDeletion",
"kms:ReEncryptTo",
"kms:CreateKey",
"kms:ConnectCustomKeyStore",
"kms:Sign",
"kms:CreateGrant",
"kms:EnableKeyRotation",
"kms:UpdateKeyDescription",
"kms:DeleteImportedKeyMaterial",
"kms:GenerateDataKeyPairWithoutPlaintext",
"kms:DisableKey",
"kms:ReEncryptFrom",
"kms:DisableKeyRotation",
"kms:RetireGrant",
"kms:VerifyMac",
"kms:UpdateAlias",
"kms:CreateCustomKeyStore",
"kms:RevokeGrant",
"kms:GenerateDataKey",
"kms:CreateAlias",
"kms:DisconnectCustomKeyStore",
"kms:DeleteAlias",
"ec2:CreateKeyPair",
"ec2:DescribeKeyPairs",
"ec2:DeleteKeyPair",
"acm:*",
"config:*",
"guardduty:*",
"inspector2:*",
"securityhub:*",
"shield:*",
"sso:*",
"organizations:*"
],
"Resource" : "*"
},
{
"Sid" : "NegateNetworkDeployerPermissions",
"Effect" : "Allow",
"NotAction" : [
"ec2:AcceptVpcEndpointConnections",
"ec2:AllocateAddress",
"ec2:AssignIpv6Addresses",
"ec2:AssignPrivateIpAddresses",
"ec2:AssociateAddress",
"ec2:AssociateDhcpOptions",
"ec2:AssociateRouteTable",
"ec2:AssociateSubnetCidrBlock",
"ec2:AssociateVpcCidrBlock",
"ec2:AttachInternetGateway",
"ec2:AttachNetworkInterface",
"ec2:AttachVpnGateway",
"ec2:CreateCarrierGateway",
"ec2:CreateCustomerGateway",
"ec2:CreateDefaultSubnet",
"ec2:CreateDefaultVpc",
"ec2:CreateDhcpOptions",
"ec2:CreateEgressOnlyInternetGateway",
"ec2:CreateFlowLogs",
"ec2:CreateInternetGateway",
"ec2:CreateNatGateway",
"ec2:CreateNetworkAcl",
"ec2:CreateNetworkAclEntry",
"ec2:CreateNetworkInterface",
"ec2:CreateNetworkInterfacePermission",
"ec2:CreatePlacementGroup",
"ec2:CreateRoute",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVpc",
"ec2:CreateVpcEndpoint",
"ec2:CreateVpcEndpointConnectionNotification",
"ec2:CreateVpcEndpointServiceConfiguration",
"ec2:CreateVpnConnection",
"ec2:CreateVpnConnectionRoute",
"ec2:CreateVpnGateway",
"ec2:DeleteCarrierGateway",
"ec2:DeleteEgressOnlyInternetGateway",
"ec2:DeleteFlowLogs",
"ec2:DeleteNatGateway",
"ec2:DeleteNetworkInterface",
"ec2:DeleteNetworkInterfacePermission",
"ec2:DeletePlacementGroup",
"ec2:DeleteSubnet",
"ec2:DeleteTags",
"ec2:DeleteVpc",
"ec2:DeleteVpcEndpointConnectionNotifications",
"ec2:DeleteVpcEndpointServiceConfigurations",
"ec2:DeleteVpcEndpoints",
"ec2:DeleteVpnConnection",
"ec2:DeleteVpnConnectionRoute",
"ec2:DeleteVpnGateway",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeCarrierGateways",
"ec2:DescribeClassicLinkInstances",
"ec2:DescribeCustomerGateways",
"ec2:DescribeDhcpOptions",
"ec2:DescribeEgressOnlyInternetGateways",
"ec2:DescribeFlowLogs",
"ec2:DescribeInstances",
"ec2:DescribeInternetGateways",
"ec2:DescribeKeyPairs",
"ec2:DescribeMovingAddresses",
"ec2:DescribeNatGateways",
"ec2:DescribeNetworkAcls",
"ec2:DescribeNetworkInterfaceAttribute",
"ec2:DescribeNetworkInterfacePermissions",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribePlacementGroups",
"ec2:DescribePrefixLists",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupReferences",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeStaleSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcClassicLink",
"ec2:DescribeVpcClassicLinkDnsSupport",
"ec2:DescribeVpcEndpointConnectionNotifications",
"ec2:DescribeVpcEndpointConnections",
"ec2:DescribeVpcEndpointServiceConfigurations",
"ec2:DescribeVpcEndpointServicePermissions",
"ec2:DescribeVpcEndpointServices",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeVpcs",
"ec2:DescribeVpnConnections",
"ec2:DescribeVpnGateways",
"ec2:DescribePublicIpv4Pools",
"ec2:DescribeIpv6Pools",
"ec2:DetachInternetGateway",
"ec2:DetachNetworkInterface",
"ec2:DetachVpnGateway",
"ec2:DisableVgwRoutePropagation",
"ec2:DisableVpcClassicLinkDnsSupport",
"ec2:DisassociateAddress",
"ec2:DisassociateRouteTable",
"ec2:DisassociateSubnetCidrBlock",
"ec2:DisassociateVpcCidrBlock",
"ec2:EnableVgwRoutePropagation",
"ec2:EnableVpcClassicLinkDnsSupport",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:ModifySecurityGroupRules",
"ec2:ModifySubnetAttribute",
"ec2:ModifyVpcAttribute",
"ec2:ModifyVpcEndpoint",
"ec2:ModifyVpcEndpointConnectionNotification",
"ec2:ModifyVpcEndpointServiceConfiguration",
"ec2:ModifyVpcEndpointServicePermissions",
"ec2:ModifyVpcPeeringConnectionOptions",
"ec2:ModifyVpcTenancy",
"ec2:MoveAddressToVpc",
"ec2:RejectVpcEndpointConnections",
"ec2:ReleaseAddress",
"ec2:ReplaceNetworkAclAssociation",
"ec2:ReplaceNetworkAclEntry",
"ec2:ReplaceRoute",
"ec2:ReplaceRouteTableAssociation",
"ec2:ResetNetworkInterfaceAttribute",
"ec2:RestoreAddressToClassic",
"ec2:UnassignIpv6Addresses",
"ec2:UnassignPrivateIpAddresses",
"ec2:UpdateSecurityGroupRuleDescriptionsEgress",
"ec2:UpdateSecurityGroupRuleDescriptionsIngress",
"ec2:AcceptVpcPeeringConnection",
"ec2:AttachClassicLinkVpc",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateVpcPeeringConnection",
"ec2:DeleteCustomerGateway",
"ec2:DeleteDhcpOptions",
"ec2:DeleteInternetGateway",
"ec2:DeleteNetworkAcl",
"ec2:DeleteNetworkAclEntry",
"ec2:DeleteRoute",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteVolume",
"ec2:DeleteVpcPeeringConnection",
"ec2:DetachClassicLinkVpc",
"ec2:DisableVpcClassicLink",
"ec2:EnableVpcClassicLink",
"ec2:GetConsoleScreenshot",
"ec2:RejectVpcPeeringConnection",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:CreateLocalGatewayRoute",
"ec2:CreateLocalGatewayRouteTableVpcAssociation",
"ec2:DeleteLocalGatewayRoute",
"ec2:DeleteLocalGatewayRouteTableVpcAssociation",
"ec2:DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations",
"ec2:DescribeLocalGatewayRouteTableVpcAssociations",
"ec2:DescribeLocalGatewayRouteTables",
"ec2:DescribeLocalGatewayVirtualInterfaceGroups",
"ec2:DescribeLocalGatewayVirtualInterfaces",
"ec2:DescribeLocalGateways",
"ec2:SearchLocalGatewayRoutes",
"ec2:AcceptTransitGatewayVpcAttachment",
"ec2:AssociateTransitGatewayRouteTable",
"ec2:CreateTransitGateway",
"ec2:CreateTransitGatewayRoute",
"ec2:CreateTransitGatewayRouteTable",
"ec2:CreateTransitGatewayVpcAttachment",
"ec2:DeleteTransitGateway",
"ec2:DeleteTransitGatewayRoute",
"ec2:DeleteTransitGatewayRouteTable",
"ec2:DeleteTransitGatewayVpcAttachment",
"ec2:DescribeTransitGatewayAttachments",
"ec2:DescribeTransitGatewayRouteTables",
"ec2:DescribeTransitGatewayVpcAttachments",
"ec2:DescribeTransitGateways",
"ec2:DisableTransitGatewayRouteTablePropagation",
"ec2:DisassociateTransitGatewayRouteTable",
"ec2:EnableTransitGatewayRouteTablePropagation",
"ec2:ExportTransitGatewayRoutes",
"ec2:GetTransitGatewayAttachmentPropagations",
"ec2:GetTransitGatewayRouteTableAssociations",
"ec2:GetTransitGatewayRouteTablePropagations",
"ec2:ModifyTransitGateway",
"ec2:ModifyTransitGatewayVpcAttachment",
"ec2:RejectTransitGatewayVpcAttachment",
"ec2:ReplaceTransitGatewayRoute",
"ec2:SearchTransitGatewayRoutes",
"route53domains:*",
"route53resolver:*",
"route53:*",
"directconnect:*"
],
"Resource" : "*"
},
{
"Sid" : "NegateDatabaseDeployerPermissions",
"Effect" : "Allow",
"NotAction" : [
"rds:*",
"redshift:*",
"elasticache:*"
],
"Resource" : "*"
},
{
"Sid" : "NegateStorageDeployerPermissions",
"Effect" : "Allow",
"NotAction" : [
"s3:*",
"ecr:*",
"elasticfilesystem:*",
"fsx:*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.CommonDeployer.id
}

View File

@ -0,0 +1,9 @@
output "devsecops-roles" {
value = [
aws_iam_role.CommonDeployer.arn,
aws_iam_role.DatabaseDeployer.arn,
aws_iam_role.NetworkDeployer.arn,
aws_iam_role.DatabaseDeployer.arn,
aws_iam_role.StorageDeployer.arn
]
}

View File

@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 3.25"
}
}
}

View File

@ -0,0 +1,12 @@
/* variable "aws-region" {}
variable "aws-region-short" {}
variable "customer-name" {}
variable "environment" {}
variable "project" {}
variable "application" {}
*/
variable max_session_duration {
type = number
default = 14400
}
variable role-trusted-entity-arn {}