diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9a459c..c733f4f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -72,3 +72,7 @@
- created policies
- policies/inf-idc-t1
- policies/inf-idc-t2
+
+* 1.5.0 -- 2026-02-12
+ - created policies
+ - policies/sc-developer
diff --git a/common/version.tf b/common/version.tf
index f549198..cf11f54 100644
--- a/common/version.tf
+++ b/common/version.tf
@@ -1,3 +1,3 @@
locals {
- _module_version = "1.4.2"
+ _module_version = "1.5.0"
}
diff --git a/policies/sc-developer/README.md b/policies/sc-developer/README.md
new file mode 100644
index 0000000..33cb3c2
--- /dev/null
+++ b/policies/sc-developer/README.md
@@ -0,0 +1,44 @@
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.12 |
+| [aws](#requirement\_aws) | >= 6.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 6.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_arn.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source |
+| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
+| [aws_iam_policy_document.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [account\_alias](#input\_account\_alias) | AWS Account Alias | `string` | `""` | no |
+| [account\_id](#input\_account\_id) | AWS Account ID (default will pull from current user) | `string` | `""` | no |
+| [override\_prefixes](#input\_override\_prefixes) | Override built-in prefixes by component. This should be used primarily for common infrastructure things | `map(string)` | `{}` | no |
+| [tags](#input\_tags) | AWS Tags to apply to appropriate resources | `map(string)` | `{}` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [customer\_managed\_policy\_names](#output\_customer\_managed\_policy\_names) | Map of policy name to permission boundary of Customer Managed Policy to attach to the permissionset |
+| [inline\_policy](#output\_inline\_policy) | AWS Policy document for the single allowed inline policy (use .json to get policy) |
+| [managed\_policy\_names](#output\_managed\_policy\_names) | Names of AWS Managed Policy to attach to the permissionset |
+| [name](#output\_name) | Permission Set Name for which all settings apply |
+| [relay\_state](#output\_relay\_state) | Relay State to pass along to permissionset |
diff --git a/policies/sc-developer/data.tf b/policies/sc-developer/data.tf
new file mode 120000
index 0000000..37fff16
--- /dev/null
+++ b/policies/sc-developer/data.tf
@@ -0,0 +1 @@
+../../common/data.tf
\ No newline at end of file
diff --git a/policies/sc-developer/defaults.tf b/policies/sc-developer/defaults.tf
new file mode 120000
index 0000000..1227df3
--- /dev/null
+++ b/policies/sc-developer/defaults.tf
@@ -0,0 +1 @@
+../../common/defaults.tf
\ No newline at end of file
diff --git a/policies/sc-developer/locals.tf b/policies/sc-developer/locals.tf
new file mode 100644
index 0000000..6aa29cd
--- /dev/null
+++ b/policies/sc-developer/locals.tf
@@ -0,0 +1,12 @@
+locals {
+ account_id = var.account_id != "" ? var.account_id : data.aws_caller_identity.current.account_id
+ account_environment = data.aws_arn.current.partition == "aws-us-gov" ? "gov" : "ew"
+ region = data.aws_region.current.region
+ region_short = join("", [for c in split("-", local.region) : substr(c, 0, 1)])
+
+ base_tags = {
+ "boc:tf_module_version" = local._module_version
+ "boc:tf_module_name" = local._module_name
+ "boc:created_by" = "terraform"
+ }
+}
diff --git a/policies/sc-developer/main.tf b/policies/sc-developer/main.tf
new file mode 100644
index 0000000..3c91f4a
--- /dev/null
+++ b/policies/sc-developer/main.tf
@@ -0,0 +1,2 @@
+/*
+*/
diff --git a/policies/sc-developer/module_name.tf b/policies/sc-developer/module_name.tf
new file mode 100644
index 0000000..3003011
--- /dev/null
+++ b/policies/sc-developer/module_name.tf
@@ -0,0 +1,3 @@
+locals {
+ _module_name = "aws-sso/policies/sc-developer"
+}
diff --git a/policies/sc-developer/outputs.tf b/policies/sc-developer/outputs.tf
new file mode 100644
index 0000000..776869b
--- /dev/null
+++ b/policies/sc-developer/outputs.tf
@@ -0,0 +1,24 @@
+output "name" {
+ description = "Permission Set Name for which all settings apply"
+ value = local.name
+}
+
+output "managed_policy_names" {
+ description = "Names of AWS Managed Policy to attach to the permissionset"
+ value = local.managed_policy_names
+}
+
+output "customer_managed_policy_names" {
+ description = "Map of policy name to permission boundary of Customer Managed Policy to attach to the permissionset"
+ value = local.customer_managed_policy_names
+}
+
+output "inline_policy" {
+ description = "AWS Policy document for the single allowed inline policy (use .json to get policy)"
+ value = local.inline_policy
+}
+
+output "relay_state" {
+ description = "Relay State to pass along to permissionset"
+ value = local.relay_state
+}
diff --git a/policies/sc-developer/policy.tf b/policies/sc-developer/policy.tf
new file mode 100644
index 0000000..5c10e6a
--- /dev/null
+++ b/policies/sc-developer/policy.tf
@@ -0,0 +1,429 @@
+data "aws_iam_policy_document" "inline" {
+ statement {
+ sid = "SSMParameters"
+ effect = "Allow"
+ actions = [
+ "ssm:DescribeParameters",
+ "ssm:GetParameter*",
+ ]
+ resources = formatlist("arn:%v:ssm:*:*:parameter%v", data.aws_arn.current.partition, [
+ "/enterprise/pki/*",
+ ])
+ }
+ statement {
+ sid = "AllowEC2NetworkInterface"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "ec2:CreateNetworkInterface",
+ "ec2:DescribeNetworkInterfaces",
+ "ec2:DeleteNetworkInterface",
+ ]
+ }
+ statement {
+ sid = "CetificateResources"
+ effect = "Allow"
+ actions = [
+ "acm:AddTagsToCertificate",
+ "acm:DeleteCertificate",
+ "acm:DescribeCertificate",
+ "acm:ExportCertificate",
+ "acm:GetCertificate",
+ "acm:ImportCertificate",
+ "acm:ListCertificates",
+ "acm:ListTagsForCertificate",
+ "acm:RemoveTagsFromCertificate",
+ "acm:RenewCertificate",
+ "acm:RequestCertificate",
+ "acm:UpdateCertificateOptions",
+ "acm-pca:GetCertificate",
+ "acm-pca:GetCertificateAuthorityCertificate",
+ "acm-pca:GetCertificateAuthorityCsr",
+ "acm-pca:IssueCertificate",
+ "acm-pca:ListTags",
+ "acm-pca:RevokeCertificate",
+ ]
+ resources = ["*"]
+ }
+ # need to exclude inf-* things
+ statement {
+ sid = "AllowServicesAllResources"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "athena:*",
+ "apigateway:*",
+ "bedrock:*",
+ "logs:*",
+ "cloudshell:*",
+ "cloudwatch:*",
+ "codebuild:*",
+ "codecommit:*",
+ "codedeploy:*",
+ "codepipeline:*",
+ "dynamodb:*",
+ "dms:*",
+ "ebs:*",
+ "ecr:*",
+ "ecs:*",
+ "eks:*",
+ "elasticfilesystem:*",
+ "elasticloadbalancing:*",
+ "firehose:*",
+ "inspector2:BatchGet*",
+ "inspector2:Describe*",
+ "inspector2:Get*",
+ "inspector2:List*",
+ "elasticloadbalancingv2:*",
+ "elasticmapreduce:*",
+ "es:*",
+ "aoss:*",
+ "glue:*",
+ "lambda:*",
+ "mq:*",
+ "rds:*",
+ "s3:*",
+ "secretsmanager:*",
+ "states:*",
+ "sqs:*",
+ "kinesis:*",
+ "transfer:*",
+ "sagemaker:*",
+ ]
+ }
+ statement {
+ sid = "AllowAutoScaling"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "application-autoscaling:RegisterScalableTarget",
+ "application-autoscaling:PutScheduledAction",
+ "application-autoscaling:PutScalingPolicy",
+ "application-autoscaling:Describe*",
+ "application-autoscaling:DeregisterScalableTarget",
+ "application-autoscaling:DeleteScheduledAction",
+ "application-autoscaling:DeleteScalingPolicy",
+ ]
+ }
+ # really needed?
+ statement {
+ sid = "AllowCognito"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "cognito-sync:*",
+ "cognito-idp:*",
+ "cognito-identity:*",
+
+ ]
+ }
+ statement {
+ sid = "AllowRoute53"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "route53:get*",
+ "route53:list*",
+ ]
+ }
+ statement {
+ sid = "AllowCloudtrailRead"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "cloudtrail:StartQuery",
+ "cloudtrail:Describe*",
+ "cloudtrail:GetTrailStatus",
+ "cloudtrail:List*",
+ "cloudtrail:ListPublicKeys",
+ "cloudtrail:Lookup*",
+ ]
+ }
+ statement {
+ sid = "AllowCFN"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "cloudformation:CreateChangeSet",
+ "cloudformation:CreateStack",
+ "cloudformation:DeleteStack",
+ "cloudformation:DescribeStackEvents",
+ "cloudformation:DescribeStackResources",
+ "cloudformation:DescribeStacks",
+ "cloudformation:GetTemplate",
+ "cloudformation:List*",
+ "cloudformation:ValidateTemplate",
+ ]
+ }
+ statement {
+ sid = "AllowEC2Actions"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "ec2:AttachNetworkInterface",
+ "ec2:AuthorizeSecurityGroupEgress",
+ "ec2:AuthorizeSecurityGroupIngress",
+ "ec2:CancelSpotInstanceRequests",
+ "ec2:CreateKeyPair",
+ "ec2:CreateNetworkInterface",
+ "ec2:CreateSecurityGroup",
+ "ec2:CreateSnapshot*",
+ "ec2:CreateTags",
+ "ec2:DeleteSnapshot",
+ "ec2:DeleteTags",
+ "ec2:Describe*",
+ "ec2:Get*",
+ "ec2:GetCoipPoolUsage",
+ "ec2:List*",
+ "ec2:ModifyImageAttribute",
+ "ec2:ModifyInstanceAttribute",
+ "ec2:ModifyNetworkInterfaceAttribute",
+ "ec2:ModifySecurityGroupRules",
+ "ec2:RequestSpotInstances",
+ "ec2:ResetNetworkInterfaceAttribute",
+ "ec2:RevokeSecurityGroupEgress",
+ "ec2:RevokeSecurityGroupIngress",
+ "ec2:UpdateSecurityGroupRuleDescriptionsEgress",
+ "ec2:UpdateSecurityGroupRuleDescriptionsIngress",
+ "ec2:St*Instances",
+ ]
+ }
+ # maybe not right now
+ statement {
+ sid = "AllowEMR"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "elasticmapreduce:*",
+ ]
+ }
+ statement {
+ sid = "AllowOpenSearch"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "es:*",
+ ]
+ }
+ statement {
+ sid = "AllowEventBusEvents"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "events:DescribeRule",
+ "events:List*",
+ "events:PutRule",
+ "events:PutTargets",
+ ]
+ }
+ statement {
+ sid = "AllowIAMActions"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "iam:ListRole*",
+ "iam:ListAttached*",
+ "iam:GetRolePolicy",
+ "iam:GetRole",
+ "iam:GetPolicyVersion",
+ "iam:GetPolicy",
+ ]
+ }
+ statement {
+ sid = "AllowIAMRoleAccess"
+ effect = "Allow"
+ resources = [
+ format(local.all_account_arn_iam, format("role/aws-service-role/%v.amazonaws.com/AWSServiceRoleFor%v", "ecs", "ECS*")),
+ format(local.all_account_arn_iam, format("role/aws-service-role/%v.amazonaws.com/AWSServiceRoleFor%v", "glue", "Glue*")),
+ format(local.all_account_arn_iam, format("role/aws-service-role/%v.amazonaws.com/AWSServiceRoleFor%v", "elasticloadbalancing", "ElasticLoadBalancing*")),
+ format(local.all_account_arn_iam, format("role/aws-service-role/%v.amazonaws.com/AWSServiceRoleFor%v", "mq", "MQ*")),
+ format(local.all_account_arn_iam, format("role/aws-service-role/%v.amazonaws.com/AWSServiceRoleFor%v", "rds", "RDS*")),
+ ]
+ actions = [
+ "iam:PutRolePolicy",
+ "iam:AttachRolePolicy",
+ ]
+ }
+ # statement {
+ # sid = "AllowServiceLinkedRoleCreate"
+ # effect = "Allow"
+ # resources = [
+ # "arn:aws-us-gov:iam::*:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
+ # "arn:aws-us-gov:iam::*:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService",
+ # "arn:aws-us-gov:iam::*:role/aws-service-role/rds.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_RDSCluster",
+ # ]
+ # actions = [
+ # "iam:CreateServiceLinkedRole",
+ # ]
+ # }
+ statement {
+ sid = "AllowIamPassRole"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "iam:PassRole"
+ ]
+ condition {
+ test = "StringEquals"
+ variable = "iam:PassedToService"
+ values = [
+ "firehose.amazonaws.com",
+ "glue.amazonaws.com",
+ "rds.amazonaws.com",
+ "s3.amazonaws.com",
+ "lambda.amazonaws.com",
+ "ecs.amazonaws.com",
+ "ecs-tasks.amazonaws.com",
+ "states.amazonaws.com",
+ "apigateway.amazonaws.com"
+ ]
+ }
+ }
+ statement {
+ sid = "AllowKMSList"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "kms:ListKeys",
+ "kms:ListKeyPolicies",
+ "kms:ListAliases",
+ "kms:GetKeyPolicy",
+ "kms:GetKeyRotationStatus",
+ "kms:ListResourceTags",
+ ]
+ }
+ # scope these to restrict inf- keys
+ statement {
+ sid = "AllowKMS"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "kms:CreateGrant",
+ "kms:CreateA*",
+ "kms:CreateK*",
+ "kms:Decrypt",
+ "kms:DescribeKey",
+ "kms:EnableK*",
+ "kms:Encrypt",
+ "kms:GenerateDataKey*",
+ "kms:ReEncrypt*",
+ "kms:RetireGrant",
+ "kms:TagResource",
+ "kms:UntagResource",
+ ]
+ }
+ statement {
+ sid = "RestrictInfKMS"
+ effect = "Deny"
+ resources = ["*"]
+ actions = ["kms:*"]
+ condition {
+ test = "StringLike"
+ variable = "kms:RequestAlias"
+ values = [
+ "alias/k-kms-inf*"
+ ]
+ }
+ }
+ statement {
+ sid = "AllowRolesAnywehereRead"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "rolesanywhere:Get*",
+ "rolesanywhere:List*",
+ ]
+ }
+ statement {
+ sid = "AllowSSMActions"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "ssm:Describe*",
+ "ssm:Get*",
+ "ssm:List*",
+ "ssm:ModifyDocumentPermission",
+ "ssm:PutInventory",
+ "ssm:PutParameter",
+ "ssm:SendCommand",
+ "ssm:StartAutomationExecution",
+ "ssm:UpdateAssocitation",
+ "ssm:UpdateAssocitationStatus",
+ "ssm:UpdateDocument*",
+ "ssm:UpdateInstance*",
+ ]
+ }
+ statement {
+ sid = "AllowSNS"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "sns:*",
+ ]
+ }
+ statement {
+ sid = "AllowStepFunctionExecution"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "states:CreateStateMachine",
+ "states:DeleteStateMachine",
+ "states:DescribeExecution",
+ "states:DescribeStateMachine",
+ "states:Get*",
+ "states:List*",
+ "states:StartExecution",
+ "states:StopExecution",
+ "states:UpdateStateMachine",
+ "states:ValidateStateMachineDefinition",
+ ]
+ }
+ statement {
+ sid = "AllowSupport"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "support:SearchForCases",
+ "support:ResolveCase",
+ "support:RateCaseCommunication",
+ "support:Describe*",
+ "support:CreateCase",
+ "support:AddCommunicationToCase",
+ "support:AddAttachmentsToSet",
+ ]
+ }
+ statement {
+ sid = "TagResources"
+ effect = "Allow"
+ resources = ["*"]
+ actions = [
+ "tag:TagResources"
+ ]
+ }
+ statement {
+ sid = "Assumerole"
+ effect = "Allow"
+ actions = ["sts:assumeRole"]
+ resources = [
+ format(local.all_account_arn_iam, "role/r-inf-terraform-route53"),
+ format(local.all_account_arn_iam, "role/r-eks-*-cluster-admin"),
+ ]
+ }
+ statement {
+ sid = "AllowBatch"
+ effect = "Allow"
+ actions = ["batch:*"]
+ resources = ["*"]
+ }
+ statement {
+ sid = "AllowEvents"
+ effect = "Allow"
+ actions = [
+ "events:*"
+ ]
+ not_resources = [
+ format("arn:%v:events:*:*:%v*", data.aws_arn.current.partition, "rule/DO_NOT_TELETE")
+ ]
+
+ }
+}
diff --git a/policies/sc-developer/prefixes.tf b/policies/sc-developer/prefixes.tf
new file mode 120000
index 0000000..5bc256c
--- /dev/null
+++ b/policies/sc-developer/prefixes.tf
@@ -0,0 +1 @@
+../../common/prefixes.tf
\ No newline at end of file
diff --git a/policies/sc-developer/settings.tf b/policies/sc-developer/settings.tf
new file mode 100644
index 0000000..7598a45
--- /dev/null
+++ b/policies/sc-developer/settings.tf
@@ -0,0 +1,12 @@
+locals {
+ name = "sc-developer"
+ description = "System Common Developer"
+ managed_policy_names = [
+ "ReadOnlyAccess",
+ ]
+ customer_managed_policy_names = {
+ "p-inf-tfstate-write" = null
+ }
+ relay_state = null
+ inline_policy = data.aws_iam_policy_document.inline
+}
diff --git a/policies/sc-developer/variables.common.tf b/policies/sc-developer/variables.common.tf
new file mode 120000
index 0000000..e01226c
--- /dev/null
+++ b/policies/sc-developer/variables.common.tf
@@ -0,0 +1 @@
+../../common/variables.common.tf
\ No newline at end of file
diff --git a/policies/sc-developer/variables.tf.unused b/policies/sc-developer/variables.tf.unused
new file mode 100644
index 0000000..53d6bf1
--- /dev/null
+++ b/policies/sc-developer/variables.tf.unused
@@ -0,0 +1,29 @@
+variable "name" {
+ description = "Permission Set Name for which all settings apply"
+ type = string
+ default = null
+}
+
+variable "managed_policy_names" {
+ description = "Names of AWS Managed Policy to attach to the permissionset"
+ type = list(string)
+ default = []
+}
+
+variable "customer_managed_policy_names" {
+ description = "Map of policy name to permission boundary of Customer Managed Policy to attach to the permissionset"
+ type = map(string)
+ default = {}
+}
+
+# variable "inline_policy" {
+# description = "AWS Policy document for the single allowed inline policy"
+# type = string
+# default = null
+# }
+
+variable "relay_state" {
+ description = "Relay State to pass along to permissionset"
+ type = string
+ default = null
+}
diff --git a/policies/sc-developer/version.tf b/policies/sc-developer/version.tf
new file mode 120000
index 0000000..4950c91
--- /dev/null
+++ b/policies/sc-developer/version.tf
@@ -0,0 +1 @@
+../../common/version.tf
\ No newline at end of file
diff --git a/policies/sc-developer/versions.tf b/policies/sc-developer/versions.tf
new file mode 120000
index 0000000..cbeda73
--- /dev/null
+++ b/policies/sc-developer/versions.tf
@@ -0,0 +1 @@
+../../common/versions.tf
\ No newline at end of file