Skip to main content
Version: 5.15.0

IaC Requirements

This document discusses the requirements for deploying the Structsure Infrastructure.

You will need the following Structsure packages:

  • Structsure Kubernetes AMI
  • Structsure IaC Modules

EC2 Jump-box

This jump-box will be used to run the Structsure IaC modules and deploy the desired Structsure configuration.

Hardware

  • 2 CPU
  • 8 GB Ram
  • 5 GB Local Storage

Tooling

Cloud Infrastructure

The Structsure IaC Modules require the following pre-created cloud resources to use as input when creating the desired Structsure configuration:

  • VPC
  • Private Subnets (two minimum for high availability)
  • Public Facing Network Load Balancers
    • Deploy Targets: one
      • One for public facing apps, such as Argo CD and deployed mission apps
    • Collaboration Environment: two
      • One for public facing apps, such as GitLab/Mattermost/Jira/Confluence
      • One dedicated for Keycloak

IAM Policy

This policy will need to be attached to the user or EC2 running the Structsure IaC Modules.

The following are the minimum IAM policies required to create and destroy Structsure infrastructure using the Structsure IaC Modules:

Deploy Target

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateLaunchTemplate",
"ec2:CreateSecurityGroup",
"ec2:CreateTags",
"ec2:DeleteKeyPair",
"ec2:DeleteLaunchTemplate",
"ec2:DeleteSecurityGroup",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInternetGateways",
"ec2:DescribeKeyPairs",
"ec2:DescribeLaunchTemplates",
"ec2:DescribeLaunchTemplateVersions",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:ImportKeyPair",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:RunInstances"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"elasticloadbalancing:AddTags",
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateTargetGroup",
"elasticloadbalancing:DeleteListener",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:DeleteTargetGroup",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeTags",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:ModifyTargetGroupAttributes",
"elasticloadbalancing:SetIpAddressType"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"sts:GetCallerIdentity"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:DeleteItem",
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:PutItem"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"kms:CreateGrant",
"kms:Decrypt",
"kms:DescribeKey",
"kms:GenerateDataKey"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:CreateBucket",
"s3:DeleteBucket",
"s3:DeleteBucketPolicy",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetAccelerateConfiguration",
"s3:GetBucketAcl",
"s3:GetBucketCORS",
"s3:GetBucketLogging",
"s3:GetBucketObjectLockConfiguration",
"s3:GetBucketOwnershipControls",
"s3:GetBucketPolicy",
"s3:GetBucketPublicAccessBlock",
"s3:GetBucketRequestPayment",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetBucketWebsite",
"s3:GetEncryptionConfiguration",
"s3:GetLifecycleConfiguration",
"s3:GetObject",
"s3:GetObjectTagging",
"s3:GetObjectVersion",
"s3:GetReplicationConfiguration",
"s3:ListAllMyBuckets",
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:PutBucketAcl",
"s3:PutBucketLogging",
"s3:PutBucketOwnershipControls",
"s3:PutBucketPolicy",
"s3:PutBucketPublicAccessBlock",
"s3:PutBucketTagging",
"s3:PutBucketVersioning",
"s3:PutEncryptionConfiguration",
"s3:PutLifecycleConfiguration",
"s3:PutObject"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"secretsmanager:CreateSecret",
"secretsmanager:TagResource"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:GetInstanceProfile",
"iam:PassRole"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"autoscaling:AttachLoadBalancerTargetGroups",
"autoscaling:CreateAutoScalingGroup",
"autoscaling:DeleteAutoScalingGroup",
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeScalingActivities",
"autoscaling:DetachLoadBalancerTargetGroups",
"autoscaling:SetInstanceProtection",
"autoscaling:TerminateInstanceInAutoScalingGroup",
"autoscaling:UpdateAutoScalingGroup"
],
"Resource": "*"
}
]
}

Collaboration Environment

In addition to the above policy, you will need the following additional IAM Policy Statement for deploying a collaboration environment:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rds:AddTagsToResource",
"rds:CreateDBCluster",
"rds:CreateDBInstance",
"rds:CreateDBSubnetGroup",
"rds:DeleteDBCluster",
"rds:DeleteDBInstance",
"rds:DeleteDBSubnetGroup",
"rds:DescribeDBClusters",
"rds:DescribeDBInstances",
"rds:DescribeDBSubnetGroups",
"rds:DescribeGlobalClusters",
"rds:ListTagsForResource"
],
"Resource": "*"
}
]
}

Agent Policies

In addition, if you are deploying to RKE2, you will need the following IAM Policy Statement:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:ModifyVolume",
"ec2:DetachVolume",
"ec2:DescribeVolumesModifications",
"ec2:DescribeVolumes",
"ec2:DescribeSecurityGroups",
"ec2:DescribeInstances",
"ec2:DeleteVolume",
"ec2:CreateVolume",
"ec2:CreateTags",
"ec2:AttachVolume"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"kms:ReEncrypt*",
"kms:GenerateRandom",
"kms:Encrypt",
"kms:DescribeKey",
"kms:Decrypt"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:ListMultipartUploadParts",
"s3:GetObject",
"s3:DeleteObject",
"s3:AbortMultipartUpload"
],
"Resource": "arn:${aws_partition}:s3:::${cluster_name}-zarf-registry/*"
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucketMultipartUploads",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": "arn:${aws_partition}:s3:::${cluster_name}-zarf-registry"
},
{
"Effect": "Allow",
"Action": [
"elasticfilesystem:DescribeMountTargets",
"elasticfilesystem:DescribeMountTargetSecurityGroups",
"elasticfilesystem:DescribeFileSystems",
"elasticfilesystem:DescribeAccessPoints",
"elasticfilesystem:CreateMountTarget",
"ec2:DescribeNetworkInterfaceAttribute",
"ec2:DescribeAvailabilityZones"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"elasticfilesystem:CreateAccessPoint",
"elasticfilesystem:DeleteAccessPoint"
],
"Condition": {
"StringLike": {
"aws:RequestTag/efs.csi.aws.com/cluster": "true"
}
},
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssm:DescribeAssociation",
"ssm:GetDeployablePatchSnapshotForInstance",
"ssm:GetDocument",
"ssm:DescribeDocument",
"ssm:GetManifest",
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:ListAssociations",
"ssm:ListInstanceAssociations",
"ssm:PutInventory",
"ssm:PutComplianceItems",
"ssm:PutConfigurePackageResult",
"ssm:UpdateAssociationStatus",
"ssm:UpdateInstanceAssociationStatus",
"ssm:UpdateInstanceInformation"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2messages:AcknowledgeMessage",
"ec2messages:DeleteMessage",
"ec2messages:FailMessage",
"ec2messages:GetEndpoint",
"ec2messages:GetMessages",
"ec2messages:SendReply"
],
"Resource": "*"
},
{
"Sid": "CWACloudWatchServerPermissions",
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData",
"ec2:DescribeVolumes",
"ec2:DescribeTags",
"logs:PutLogEvents",
"logs:PutRetentionPolicy",
"logs:DescribeLogStreams",
"logs:DescribeLogGroups",
"logs:CreateLogStream",
"logs:CreateLogGroup",
"xray:PutTraceSegments",
"xray:PutTelemetryRecords",
"xray:GetSamplingRules",
"xray:GetSamplingTargets",
"xray:GetSamplingStatisticSummaries"
],
"Resource": "*"
},
{
"Sid": "CWASSMServerPermissions",
"Effect": "Allow",
"Action": [
"ssm:GetParameter"
],
"Resource": "arn:aws:ssm:*:*:parameter/AmazonCloudWatch-*"
},
{
"Sid": "GitLabBucketPermissions",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts",
"s3:ListBucketMultipartUploads"
],
"Resource": [
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-artifacts",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-backup",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-backup-tmp",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-dependency-proxy",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-lfs",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-mr-diffs",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-packages",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-pseudo",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-registry",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-runners-cache",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-terraform-state",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-uploads"
]
},
{
"Sid": "GitlabObjectPermissions",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-artifacts/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-backup/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-backup-tmp/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-dependency-proxy/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-lfs/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-mr-diffs/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-packages/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-pseudo/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-registry/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-runners-cache/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-terraform-state/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-uploads/*"
]
},
{
"Sid": "DockerBucketPermissions",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:ListBucketMultipartUploads"
],
"Resource": [
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-artifacts",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-backup",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-backup-tmp",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-dependency-proxy",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-lfs",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-mr-diffs",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-packages",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-pseudo",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-registry",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-runners-cache",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-terraform-state",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-uploads"
]
},
{
"Sid": "DockerObjectPermissions",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListMultipartUploadParts",
"s3:AbortMultipartUpload"
],
"Resource": [
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-artifacts/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-backup/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-backup-tmp/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-dependency-proxy/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-lfs/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-mr-diffs/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-packages/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-pseudo/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-registry/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-runners-cache/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-terraform-state/*",
"arn:${aws_partition}:s3:::${cluster_name}-gitlab-uploads/*"
]
},
{
"Sid": "LokiBucketPermissions",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts",
"s3:ListBucketMultipartUploads",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:${aws_partition}:s3:::${cluster_name}-loki-admin",
"arn:${aws_partition}:s3:::${cluster_name}-loki-logs",
"arn:${aws_partition}:s3:::${cluster_name}-loki-ruler"
]
},
{
"Sid": "LokiObjectPermissions",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObjectAcl",
"s3:ListMultipartUploadParts",
"s3:AbortMultipartUpload"
],
"Resource": [
"arn:${aws_partition}:s3:::${cluster_name}-loki-admin/*",
"arn:${aws_partition}:s3:::${cluster_name}-loki-logs/*",
"arn:${aws_partition}:s3:::${cluster_name}-loki-ruler/*"
]
},
{
"Sid": "MattermostBucketPermissions",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": "arn:${aws_partition}:s3:::${cluster_name}-mattermost"
},
{
"Sid": "MattermostObjectPermissions",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:${aws_partition}:s3:::${cluster_name}-mattermost/*"
},
{
"Sid": "VeleroBucketPermissions",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": "arn:${aws_partition}:s3:::${cluster_name}-velero"
},
{
"Sid": "VeleroObjectPermissions",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:DeleteObject",
"s3:PutObject",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts"
],
"Resource": "arn:${aws_partition}:s3:::${cluster_name}-velero/*"
},
{
"Sid": "VeleroEBSBackupPermissions",
"Effect": "Allow",
"Action": [
"ec2:DescribeVolumes",
"ec2:DescribeSnapshots",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:CreateSnapshot",
"ec2:DeleteSnapshot"
],
"Resource": "*"
}
]
}

Control Plane Policies

The control plane nodes need the same policies as the agent nodes, plus the following:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:ListBucket",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": [
"arn:${aws_partition}:s3:::${cluster_name}-rke2/etcd-snapshots/*",
"arn:${aws_partition}:s3:::${cluster_name}-rke2"
]
}
]
}

EKS Policies

If you are using an existing EKS cluster IAM role rather than letting Structsure create it for you the role will need the following policies attached:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:UpdateAutoScalingGroup",
"ec2:AttachVolume",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateRoute",
"ec2:CreateSecurityGroup",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:DeleteRoute",
"ec2:DeleteSecurityGroup",
"ec2:DeleteVolume",
"ec2:DescribeInstances",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVolumes",
"ec2:DescribeVolumesModifications",
"ec2:DescribeVpcs",
"ec2:DescribeDhcpOptions",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeAvailabilityZones",
"ec2:DetachVolume",
"ec2:ModifyInstanceAttribute",
"ec2:ModifyVolume",
"ec2:RevokeSecurityGroupIngress",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeInternetGateways",
"elasticloadbalancing:AddTags",
"elasticloadbalancing:ApplySecurityGroupsToLoadBalancer",
"elasticloadbalancing:AttachLoadBalancerToSubnets",
"elasticloadbalancing:ConfigureHealthCheck",
"elasticloadbalancing:CreateListener",
"elasticloadbalancing:CreateLoadBalancer",
"elasticloadbalancing:CreateLoadBalancerListeners",
"elasticloadbalancing:CreateLoadBalancerPolicy",
"elasticloadbalancing:CreateTargetGroup",
"elasticloadbalancing:DeleteListener",
"elasticloadbalancing:DeleteLoadBalancer",
"elasticloadbalancing:DeleteLoadBalancerListeners",
"elasticloadbalancing:DeleteTargetGroup",
"elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
"elasticloadbalancing:DeregisterTargets",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeLoadBalancerPolicies",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:DetachLoadBalancerFromSubnets",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:ModifyLoadBalancerAttributes",
"elasticloadbalancing:ModifyTargetGroup",
"elasticloadbalancing:ModifyTargetGroupAttributes",
"elasticloadbalancing:RegisterInstancesWithLoadBalancer",
"elasticloadbalancing:RegisterTargets",
"elasticloadbalancing:SetLoadBalancerPoliciesForBackendServer",
"elasticloadbalancing:SetLoadBalancerPoliciesOfListener",
"kms:DescribeKey"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:AWSServiceName": "elasticloadbalancing.amazonaws.com"
}
}
},
{
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ListGrants",
"kms:DescribeKey",
"kms:ReEncrypt*"
],
"Effect": "Allow",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"autoscaling:SetDesiredCapacity",
"autoscaling:TerminateInstanceInAutoScalingGroup"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingInstances",
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeScalingActivities",
"ec2:DescribeLaunchTemplateVersions",
"autoscaling:DescribeTags",
"autoscaling:DescribeLaunchConfigurations",
"ec2:DescribeInstanceTypes"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ec2:CreateNetworkInterfacePermission",
"Resource": "*",
"Condition": {
"ForAnyValue:StringEquals": {
"ec2:ResourceTag/eks:eni:owner": "eks-vpc-resource-controller"
}
}
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DetachNetworkInterface",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:DeleteNetworkInterface",
"ec2:AttachNetworkInterface",
"ec2:UnassignPrivateIpAddresses",
"ec2:AssignPrivateIpAddresses"
],
"Resource": "*"
}
]
}

In addition, ensure that the Trusted entities under the Trust relationships tab on the IAM Role page has the following:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "eks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}

Using ec2.amazonaws.com instead of eks.amazonaws.com could cause permission failures.