diff --git a/.github/workflows/terraform-test.yaml b/.github/workflows/terraform-test.yaml index f05e85e..edc2a6b 100644 --- a/.github/workflows/terraform-test.yaml +++ b/.github/workflows/terraform-test.yaml @@ -23,8 +23,6 @@ jobs: PR_NUMBER: >- ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.pr_number || github.event.pull_request.number }} - - steps: - name: Checkout PR code uses: actions/checkout@v4 @@ -32,20 +30,20 @@ jobs: ref: refs/pull/${{ env.PR_NUMBER }}/head - name: Configure AWS credentials via OIDC - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@v2 with: - role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + role-to-assume: ${{ secrets.ARC_IAC_TERRATEST_ROLE }} aws-region: us-east-1 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v4 with: go-version: '1.24' - name: Set up Terraform - uses: hashicorp/setup-terraform@v3 + uses: hashicorp/setup-terraform@v2 with: - terraform_version: 1.5.7 + terraform_version: 1.7.5 terraform_wrapper: false - name: Create test directory and download go from S3 @@ -60,7 +58,7 @@ jobs: go get github.com/gruntwork-io/terratest/modules/terraform go get github.com/stretchr/testify/assert go mod tidy - go test -v -timeout 60m + go test -v -timeout 50m - name: Report check status manually uses: actions/github-script@v7 with: diff --git a/.snyk b/.snyk deleted file mode 100644 index 92984b5..0000000 --- a/.snyk +++ /dev/null @@ -1,2 +0,0 @@ -# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. -# ignores vulnerabilities until expiry date; change duration by modifying expiry date diff --git a/README.md b/README.md index 905e29f..5007f0f 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,37 @@ ## Overview -The ARC AWS SageMaker Terraform module provides a robust and fully extensible solution for managing SageMaker Studio domains, user profiles, models, endpoint, endpoint configurations, and pipelines. It supports custom app settings, and VPC integration to meet enterprise-grade machine learning needs. With dynamic configurations and secure defaults, the module enables rapid, consistent, and scalable SageMaker infrastructure provisioning using best practices. +The ARC Terraform module provides a robust and fully extensible solution for managing SageMaker Studio domains, user profiles, models, endpoint, endpoint configurations, and pipelines. It supports custom app settings, and VPC integration to meet enterprise-grade machine learning needs. With dynamic configurations and secure defaults, the module enables rapid, consistent, and scalable SageMaker infrastructure provisioning using best practices. -## Usage +### Prerequisites +Before using this module, ensure you have the following: + +- AWS credentials configured. +- Terraform installed. +- A working knowledge of Terraform. + +## Getting Started + +1. **Define the Module** + +Initially, it's essential to define a Terraform module, which is organized as a distinct directory encompassing Terraform configuration files. Within this module directory, input variables and output values must be defined in the variables.tf and outputs.tf files, respectively. The following illustrates an example directory structure: + + + +```plaintext +sagemaker/ +|-- main.tf +|-- variables.tf +|-- outputs.tf +``` + + +2. **Define Input Variables** -To see a full examples, check out the [main.tf](./examples/endpoint/main.tf) file in the example folder. +Inside the `variables.tf` or in `*.tfvars` file, you should define values for the variables that the module requires. + +3. **Use the Module in Your Main Configuration** +In your main Terraform configuration file (e.g., main.tf), you can use the module. Specify the source of the module, and version, For Example ```hcl module "sagemaker_model" { @@ -43,6 +69,81 @@ module "sagemaker_model" { } ``` +4. **Output Values** + +Inside the `outputs.tf` file of the module, you can define output values that can be referenced in the main configuration. For example: + +```hcl +output "model_name" { + description = "Name of the SageMaker model" + value = module.sagemaker_model.model_name +} + +output "model_arn" { + description = "ARN of the SageMaker model" + value = module.sagemaker_model.model_arn +} + +output "endpoint_config_arn" { + description = "ARN of the SageMaker endpoint configuration" + value = module.sagemaker_model.endpoint_config_arn +} + +``` + +5. **.tfvars** + +Inside the `.tfvars` file of the module, you can provide desired values that can be referenced in the main configuration. + + +## First Time Usage +***uncomment the backend block in [main.tf](./examples/endpoint//main.tf)*** +```shell +terraform init -backend-config=config.dev.hcl +``` +***If testing locally, `terraform init` should be fine*** + +Create a `dev` workspace +```shell +terraform workspace new dev +``` + +Plan Terraform +```shell +terraform plan -var-file dev.tfvars +``` + +Apply Terraform +```shell +terraform apply -var-file dev.tfvars +``` + +## Production Setup +```shell +terraform init -backend-config=config.prod.hcl +``` + +Create a `prod` workspace +```shell +terraform workspace new prod +``` + +Plan Terraform +```shell +terraform plan -var-file prod.tfvars +``` + +Apply Terraform +```shell +terraform apply -var-file prod.tfvars +``` + +## Cleanup +Destroy Terraform +```shell +terraform destroy -var-file dev.tfvars +``` + ## Requirements @@ -100,6 +201,7 @@ module "sagemaker_model" { | [create\_model](#input\_create\_model) | Whether to create the SageMaker model | `bool` | `false` | no | | [create\_pipeline](#input\_create\_pipeline) | Whether to create the SageMaker pipeline | `bool` | `false` | no | | [create\_pipeline\_role](#input\_create\_pipeline\_role) | Whether to create a separate role for pipelines | `bool` | `false` | no | +| [create\_sagemaker\_execution\_role](#input\_create\_sagemaker\_execution\_role) | Whether to create an SageMaker execution role | `bool` | `false` | no | | [create\_security\_groups](#input\_create\_security\_groups) | Whether to create security groups for SageMaker Studio | `bool` | `false` | no | | [create\_user\_profile](#input\_create\_user\_profile) | Whether to create the SageMaker user profile | `bool` | `false` | no | | [data\_capture\_config](#input\_data\_capture\_config) | (Optional) Configuration for capturing input/output data. |
object({
initial_sampling_percentage = number
destination_s3_uri = string
kms_key_id = optional(string)
enable_capture = optional(bool)
capture_options = list(object({
capture_mode = string
}))
capture_content_type_header = optional(object({
csv_content_types = optional(list(string))
json_content_types = optional(list(string))
}))
}) | `null` | no |
diff --git a/examples/endpoint/main.tf b/examples/endpoint/main.tf
index cf42f7f..6c7146b 100644
--- a/examples/endpoint/main.tf
+++ b/examples/endpoint/main.tf
@@ -31,9 +31,10 @@ module "tags" {
module "sagemaker_model" {
source = "../.."
- name = "terraform-arc"
- create_endpoint_config = true
- create_model = true
+ name = "terraform-arc"
+ create_endpoint_config = true
+ create_model = true
+ create_sagemaker_execution_role = true
primary_container = {
diff --git a/examples/endpoint/terraform.tfvars b/examples/endpoint/terraform.tfvars
index 3189d63..0d253dd 100644
--- a/examples/endpoint/terraform.tfvars
+++ b/examples/endpoint/terraform.tfvars
@@ -1,4 +1 @@
-# Complete SageMaker Example Configuration
-# This file demonstrates how to enable all SageMaker components: Model/Endpoint, Pipeline, and Studio
-
region = "us-east-1"
diff --git a/examples/multi-user/README.md b/examples/multi-user/README.md
index e69de29..80fcb6f 100644
--- a/examples/multi-user/README.md
+++ b/examples/multi-user/README.md
@@ -0,0 +1,56 @@
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.5.0 |
+| [aws](#requirement\_aws) | ~> 5.0 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | 5.100.0 |
+
+## Modules
+
+| Name | Source | Version |
+|------|--------|---------|
+| [sagemaker\_studio\_multi\_user](#module\_sagemaker\_studio\_multi\_user) | ../../ | n/a |
+| [tags](#module\_tags) | sourcefuse/arc-tags/aws | 1.2.6 |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_iam_role.sagemaker_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy_attachment.attach_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_subnets.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets) | data source |
+| [aws_vpc.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [create\_execution\_role](#input\_create\_execution\_role) | Whether to create a default execution role | `bool` | `false` | no |
+| [environment](#input\_environment) | Environment name (dev, staging, prod) | `string` | `"dev"` | no |
+| [input\_data\_s3\_uri](#input\_input\_data\_s3\_uri) | S3 URI for input data in shared pipeline | `string` | n/a | yes |
+| [kms\_key\_id](#input\_kms\_key\_id) | KMS key ID for encryption | `string` | `null` | no |
+| [namespace](#input\_namespace) | Namespace of the project, i.e. arc | `string` | n/a | yes |
+| [output\_data\_s3\_path](#input\_output\_data\_s3\_path) | S3 path for pipeline output data | `string` | n/a | yes |
+| [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no |
+| [security\_group\_data](#input\_security\_group\_data) | (optional) Security Group data | object({
security_group_ids_to_attach = optional(list(string), [])
create = optional(bool, true)
description = optional(string, null)
ingress_rules = optional(list(object({
description = optional(string, null)
cidr_block = optional(string, null)
source_security_group_id = optional(string, null)
from_port = number
ip_protocol = string
to_port = string
self = optional(bool, false)
})), [])
egress_rules = optional(list(object({
description = optional(string, null)
cidr_block = optional(string, null)
destination_security_group_id = optional(string, null)
from_port = number
ip_protocol = string
to_port = string
prefix_list_id = optional(string, null)
})), [])
}) | {
"create": false
} | no |
+| [security\_group\_name](#input\_security\_group\_name) | sagemaker security group name | `string` | n/a | yes |
+| [shared\_s3\_path](#input\_shared\_s3\_path) | S3 path for shared notebook outputs | `string` | n/a | yes |
+| [subnet\_names](#input\_subnet\_names) | List of subnet names to lookup | `list(string)` | [| no | +| [vpc\_name](#input\_vpc\_name) | Name of the VPC to add the resources | `string` | `"arc-poc-vpc"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [domain\_id](#output\_domain\_id) | SageMaker Domain ID | +| [domain\_url](#output\_domain\_url) | URL to access SageMaker Studio | +| [pipeline\_arns](#output\_pipeline\_arns) | Map of pipeline names to their ARNs | +| [user\_profiles](#output\_user\_profiles) | Map of user profile names to their ARNs | + diff --git a/iam.tf b/iam.tf index e0d4429..7e69d3c 100644 --- a/iam.tf +++ b/iam.tf @@ -1,5 +1,6 @@ resource "aws_iam_role" "sagemaker_execution_role" { - name = "${var.name}-sagemaker-execution-role" + count = var.create_sagemaker_execution_role ? 1 : 0 + name = "${var.name}-sagemaker-execution-role" assume_role_policy = jsonencode({ Version = "2012-10-17" @@ -14,7 +15,8 @@ resource "aws_iam_role" "sagemaker_execution_role" { } resource "aws_iam_role_policy_attachment" "attach_policy" { - role = aws_iam_role.sagemaker_execution_role.name + count = var.create_sagemaker_execution_role ? 1 : 0 + role = aws_iam_role.sagemaker_execution_role[0].name policy_arn = "arn:aws:iam::aws:policy/AmazonSageMakerFullAccess" } # IAM Role for SageMaker Execution diff --git a/main.tf b/main.tf index 898f45f..d3e2967 100644 --- a/main.tf +++ b/main.tf @@ -4,7 +4,7 @@ resource "aws_sagemaker_model" "this" { count = var.create_model ? 1 : 0 name = var.name - execution_role_arn = aws_iam_role.sagemaker_execution_role.arn + execution_role_arn = aws_iam_role.sagemaker_execution_role[0].arn tags = var.tags dynamic "primary_container" { @@ -362,7 +362,7 @@ resource "aws_sagemaker_domain" "this" { for_each = jupyter_lab_app_settings.value.emr_settings != null ? [jupyter_lab_app_settings.value.emr_settings] : [] content { assumable_role_arns = emr_settings.value.assumable_role_arns - execution_role_arns = aws_iam_role.execution_role.arn + execution_role_arns = emr_settings.value.execution_role } } } diff --git a/variables.tf b/variables.tf index 5c9a892..bef15c2 100644 --- a/variables.tf +++ b/variables.tf @@ -850,6 +850,12 @@ variable "pipelines" { default = [] } +variable "create_sagemaker_execution_role" { + description = "Whether to create an SageMaker execution role" + type = bool + default = false +} + # IAM Configuration variable "create_execution_role" { description = "Whether to create an execution role for SageMaker"
"arc-poc-private-subnet-private-us-east-1a",
"arc-poc-private-subnet-private-us-east-1b"
]