From 31f9e9143e236c126897111281062f6ad0f4fe5d Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 26 Nov 2019 12:59:21 -0500 Subject: [PATCH] New Resource: aws_eks_fargate_profile Output from acceptance testing in AWS Commercial (us-east-2): ``` --- PASS: TestAccAWSEksFargateProfile_basic (1487.63s) --- PASS: TestAccAWSEksFargateProfile_disappears (1496.90s) --- PASS: TestAccAWSEksFargateProfile_Selector_Labels (1482.71s) --- PASS: TestAccAWSEksFargateProfile_Tags (1420.50s) ``` --- aws/provider.go | 1 + aws/resource_aws_eks_fargate_profile.go | 351 +++++++++++++ aws/resource_aws_eks_fargate_profile_test.go | 491 ++++++++++++++++++ website/aws.erb | 3 + .../docs/r/eks_fargate_profile.html.markdown | 97 ++++ 5 files changed, 943 insertions(+) create mode 100644 aws/resource_aws_eks_fargate_profile.go create mode 100644 aws/resource_aws_eks_fargate_profile_test.go create mode 100644 website/docs/r/eks_fargate_profile.html.markdown diff --git a/aws/provider.go b/aws/provider.go index 24ebb463bb0..d17a805318f 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -502,6 +502,7 @@ func Provider() terraform.ResourceProvider { "aws_eip": resourceAwsEip(), "aws_eip_association": resourceAwsEipAssociation(), "aws_eks_cluster": resourceAwsEksCluster(), + "aws_eks_fargate_profile": resourceAwsEksFargateProfile(), "aws_eks_node_group": resourceAwsEksNodeGroup(), "aws_elasticache_cluster": resourceAwsElasticacheCluster(), "aws_elasticache_parameter_group": resourceAwsElasticacheParameterGroup(), diff --git a/aws/resource_aws_eks_fargate_profile.go b/aws/resource_aws_eks_fargate_profile.go new file mode 100644 index 00000000000..939dda3bc66 --- /dev/null +++ b/aws/resource_aws_eks_fargate_profile.go @@ -0,0 +1,351 @@ +package aws + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func resourceAwsEksFargateProfile() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsEksFargateProfileCreate, + Read: resourceAwsEksFargateProfileRead, + Update: resourceAwsEksFargateProfileUpdate, + Delete: resourceAwsEksFargateProfileDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "cluster_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + "fargate_profile_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + "pod_execution_role_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + "selector": { + Type: schema.TypeSet, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "labels": { + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "namespace": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "subnet_ids": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "tags": tagsSchema(), + }, + } +} + +func resourceAwsEksFargateProfileCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).eksconn + clusterName := d.Get("cluster_name").(string) + fargateProfileName := d.Get("fargate_profile_name").(string) + id := fmt.Sprintf("%s:%s", clusterName, fargateProfileName) + + input := &eks.CreateFargateProfileInput{ + ClientRequestToken: aws.String(resource.UniqueId()), + ClusterName: aws.String(clusterName), + FargateProfileName: aws.String(fargateProfileName), + PodExecutionRoleArn: aws.String(d.Get("pod_execution_role_arn").(string)), + Selectors: expandEksFargateProfileSelectors(d.Get("selector").(*schema.Set).List()), + Subnets: expandStringSet(d.Get("subnet_ids").(*schema.Set)), + } + + if v := d.Get("tags").(map[string]interface{}); len(v) > 0 { + input.Tags = keyvaluetags.New(v).IgnoreAws().EksTags() + } + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + _, err := conn.CreateFargateProfile(input) + + // Retry for IAM eventual consistency on error: + // InvalidParameterException: Misconfigured PodExecutionRole Trust Policy; Please add the eks-fargate-pods.amazonaws.com Service Principal + if isAWSErr(err, eks.ErrCodeInvalidParameterException, "Misconfigured PodExecutionRole Trust Policy") { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.CreateFargateProfile(input) + } + + if err != nil { + return fmt.Errorf("error creating EKS Fargate Profile (%s): %s", id, err) + } + + d.SetId(id) + + stateConf := resource.StateChangeConf{ + Pending: []string{eks.FargateProfileStatusCreating}, + Target: []string{eks.FargateProfileStatusActive}, + Timeout: d.Timeout(schema.TimeoutCreate), + Refresh: refreshEksFargateProfileStatus(conn, clusterName, fargateProfileName), + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("error waiting for EKS Fargate Profile (%s) creation: %s", d.Id(), err) + } + + return resourceAwsEksFargateProfileRead(d, meta) +} + +func resourceAwsEksFargateProfileRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).eksconn + + clusterName, fargateProfileName, err := resourceAwsEksFargateProfileParseId(d.Id()) + if err != nil { + return err + } + + input := &eks.DescribeFargateProfileInput{ + ClusterName: aws.String(clusterName), + FargateProfileName: aws.String(fargateProfileName), + } + + output, err := conn.DescribeFargateProfile(input) + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] EKS Fargate Profile (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading EKS Fargate Profile (%s): %s", d.Id(), err) + } + + fargateProfile := output.FargateProfile + + if fargateProfile == nil { + log.Printf("[WARN] EKS Fargate Profile (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("arn", fargateProfile.FargateProfileArn) + d.Set("cluster_name", fargateProfile.ClusterName) + d.Set("fargate_profile_name", fargateProfile.FargateProfileName) + d.Set("pod_execution_role_arn", fargateProfile.PodExecutionRoleArn) + + if err := d.Set("selector", flattenEksFargateProfileSelectors(fargateProfile.Selectors)); err != nil { + return fmt.Errorf("error setting selector: %s", err) + } + + d.Set("status", fargateProfile.Status) + + if err := d.Set("subnet_ids", aws.StringValueSlice(fargateProfile.Subnets)); err != nil { + return fmt.Errorf("error setting subnets: %s", err) + } + + if err := d.Set("tags", keyvaluetags.EksKeyValueTags(fargateProfile.Tags).IgnoreAws().Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + return nil +} + +func resourceAwsEksFargateProfileUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).eksconn + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.EksUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } + } + + return resourceAwsEksFargateProfileRead(d, meta) +} + +func resourceAwsEksFargateProfileDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).eksconn + + clusterName, fargateProfileName, err := resourceAwsEksFargateProfileParseId(d.Id()) + if err != nil { + return err + } + + input := &eks.DeleteFargateProfileInput{ + ClusterName: aws.String(clusterName), + FargateProfileName: aws.String(fargateProfileName), + } + + _, err = conn.DeleteFargateProfile(input) + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting EKS Fargate Profile (%s): %s", d.Id(), err) + } + + if err := waitForEksFargateProfileDeletion(conn, clusterName, fargateProfileName, d.Timeout(schema.TimeoutDelete)); err != nil { + return fmt.Errorf("error waiting for EKS Fargate Profile (%s) deletion: %s", d.Id(), err) + } + + return nil +} + +func expandEksFargateProfileSelectors(l []interface{}) []*eks.FargateProfileSelector { + if len(l) == 0 { + return nil + } + + fargateProfileSelectors := make([]*eks.FargateProfileSelector, 0, len(l)) + + for _, mRaw := range l { + m, ok := mRaw.(map[string]interface{}) + + if !ok { + continue + } + + fargateProfileSelector := &eks.FargateProfileSelector{} + + if v, ok := m["labels"].(map[string]interface{}); ok && len(v) > 0 { + fargateProfileSelector.Labels = stringMapToPointers(v) + } + + if v, ok := m["namespace"].(string); ok && v != "" { + fargateProfileSelector.Namespace = aws.String(v) + } + + fargateProfileSelectors = append(fargateProfileSelectors, fargateProfileSelector) + } + + return fargateProfileSelectors +} + +func flattenEksFargateProfileSelectors(fargateProfileSelectors []*eks.FargateProfileSelector) []map[string]interface{} { + if len(fargateProfileSelectors) == 0 { + return []map[string]interface{}{} + } + + l := make([]map[string]interface{}, 0, len(fargateProfileSelectors)) + + for _, fargateProfileSelector := range fargateProfileSelectors { + m := map[string]interface{}{ + "labels": aws.StringValueMap(fargateProfileSelector.Labels), + "namespace": aws.StringValue(fargateProfileSelector.Namespace), + } + + l = append(l, m) + } + + return l +} + +func refreshEksFargateProfileStatus(conn *eks.EKS, clusterName string, fargateProfileName string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &eks.DescribeFargateProfileInput{ + ClusterName: aws.String(clusterName), + FargateProfileName: aws.String(fargateProfileName), + } + + output, err := conn.DescribeFargateProfile(input) + + if err != nil { + return "", "", err + } + + fargateProfile := output.FargateProfile + + if fargateProfile == nil { + return fargateProfile, "", fmt.Errorf("EKS Fargate Profile (%s:%s) missing", clusterName, fargateProfileName) + } + + return fargateProfile, aws.StringValue(fargateProfile.Status), nil + } +} + +func waitForEksFargateProfileDeletion(conn *eks.EKS, clusterName string, fargateProfileName string, timeout time.Duration) error { + stateConf := resource.StateChangeConf{ + Pending: []string{ + eks.FargateProfileStatusActive, + eks.FargateProfileStatusDeleting, + }, + Target: []string{""}, + Timeout: timeout, + Refresh: refreshEksFargateProfileStatus(conn, clusterName, fargateProfileName), + } + + _, err := stateConf.WaitForState() + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + return nil + } + + return err +} + +func resourceAwsEksFargateProfileParseId(id string) (string, string, error) { + parts := strings.Split(id, ":") + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%s), expected cluster-name:fargate-profile-name", id) + } + + return parts[0], parts[1], nil +} diff --git a/aws/resource_aws_eks_fargate_profile_test.go b/aws/resource_aws_eks_fargate_profile_test.go new file mode 100644 index 00000000000..03deb4f1a15 --- /dev/null +++ b/aws/resource_aws_eks_fargate_profile_test.go @@ -0,0 +1,491 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccAWSEksFargateProfile_basic(t *testing.T) { + var fargateProfile eks.FargateProfile + rName := acctest.RandomWithPrefix("tf-acc-test") + eksClusterResourceName := "aws_eks_cluster.test" + iamRoleResourceName := "aws_iam_role.pod" + resourceName := "aws_eks_fargate_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEksFargateProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksFargateProfileConfigFargateProfileName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksFargateProfileExists(resourceName, &fargateProfile), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "eks", regexp.MustCompile(fmt.Sprintf("fargateprofile/%[1]s/%[1]s/.+", rName))), + resource.TestCheckResourceAttrPair(resourceName, "cluster_name", eksClusterResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "fargate_profile_name", rName), + resource.TestCheckResourceAttrPair(resourceName, "pod_execution_role_arn", iamRoleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "selector.#", "1"), + resource.TestCheckResourceAttr(resourceName, "status", eks.FargateProfileStatusActive), + resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEksFargateProfile_disappears(t *testing.T) { + var fargateProfile eks.FargateProfile + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_fargate_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEksFargateProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksFargateProfileConfigFargateProfileName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksFargateProfileExists(resourceName, &fargateProfile), + testAccCheckAWSEksFargateProfileDisappears(&fargateProfile), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSEksFargateProfile_Selector_Labels(t *testing.T) { + var fargateProfile1 eks.FargateProfile + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_fargate_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEksFargateProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksFargateProfileConfigSelectorLabels1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksFargateProfileExists(resourceName, &fargateProfile1), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEksFargateProfile_Tags(t *testing.T) { + var fargateProfile1, fargateProfile2, fargateProfile3 eks.FargateProfile + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_eks_fargate_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEks(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEksFargateProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEksFargateProfileConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksFargateProfileExists(resourceName, &fargateProfile1), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSEksFargateProfileConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksFargateProfileExists(resourceName, &fargateProfile2), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSEksFargateProfileConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEksFargateProfileExists(resourceName, &fargateProfile3), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccCheckAWSEksFargateProfileExists(resourceName string, fargateProfile *eks.FargateProfile) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EKS Fargate Profile ID is set") + } + + clusterName, fargateProfileName, err := resourceAwsEksFargateProfileParseId(rs.Primary.ID) + if err != nil { + return err + } + + conn := testAccProvider.Meta().(*AWSClient).eksconn + + input := &eks.DescribeFargateProfileInput{ + ClusterName: aws.String(clusterName), + FargateProfileName: aws.String(fargateProfileName), + } + + output, err := conn.DescribeFargateProfile(input) + + if err != nil { + return err + } + + if output == nil || output.FargateProfile == nil { + return fmt.Errorf("EKS Fargate Profile (%s) not found", rs.Primary.ID) + } + + if aws.StringValue(output.FargateProfile.FargateProfileName) != fargateProfileName { + return fmt.Errorf("EKS Fargate Profile (%s) not found", rs.Primary.ID) + } + + if got, want := aws.StringValue(output.FargateProfile.Status), eks.FargateProfileStatusActive; got != want { + return fmt.Errorf("EKS Fargate Profile (%s) not in %s status, got: %s", rs.Primary.ID, want, got) + } + + *fargateProfile = *output.FargateProfile + + return nil + } +} + +func testAccCheckAWSEksFargateProfileDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).eksconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_eks_fargate_profile" { + continue + } + + clusterName, fargateProfileName, err := resourceAwsEksFargateProfileParseId(rs.Primary.ID) + if err != nil { + return err + } + + input := &eks.DescribeFargateProfileInput{ + ClusterName: aws.String(clusterName), + FargateProfileName: aws.String(fargateProfileName), + } + + output, err := conn.DescribeFargateProfile(input) + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + continue + } + + if output != nil && output.FargateProfile != nil && aws.StringValue(output.FargateProfile.FargateProfileName) == fargateProfileName { + return fmt.Errorf("EKS Fargate Profile (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSEksFargateProfileDisappears(fargateProfile *eks.FargateProfile) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).eksconn + + input := &eks.DeleteFargateProfileInput{ + ClusterName: fargateProfile.ClusterName, + FargateProfileName: fargateProfile.FargateProfileName, + } + + _, err := conn.DeleteFargateProfile(input) + + if isAWSErr(err, eks.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return err + } + + return waitForEksFargateProfileDeletion(conn, aws.StringValue(fargateProfile.ClusterName), aws.StringValue(fargateProfile.FargateProfileName), 10*time.Minute) + } +} + +func testAccAWSEksFargateProfileConfigBase(rName string) string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" { + state = "available" +} + +data "aws_partition" "current" {} + +resource "aws_iam_role" "cluster" { + name = "%[1]s-cluster" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "eks.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_iam_role_policy_attachment" "cluster-AmazonEKSClusterPolicy" { + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonEKSClusterPolicy" + role = aws_iam_role.cluster.name +} + +resource "aws_iam_role_policy_attachment" "cluster-AmazonEKSServicePolicy" { + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonEKSServicePolicy" + role = aws_iam_role.cluster.name +} + +resource "aws_iam_role" "pod" { + name = "%[1]s-pod" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "eks-fargate-pods.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_iam_role_policy_attachment" "pod-AmazonEKSFargatePodExecutionRolePolicy" { + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy" + role = aws_iam_role.pod.name +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "tf-acc-test-eks-fargate-profile" + "kubernetes.io/cluster/%[1]s" = "shared" + } +} + +resource "aws_internet_gateway" "test" { + vpc_id = aws_vpc.test.id +} + +resource "aws_route_table" "public" { + vpc_id = aws_vpc.test.id + + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.test.id + } +} + +resource "aws_main_route_table_association" "test" { + route_table_id = aws_route_table.public.id + vpc_id = aws_vpc.test.id +} + +resource "aws_subnet" "private" { + count = 2 + + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, count.index+2) + vpc_id = aws_vpc.test.id + + tags = { + Name = "tf-acc-test-eks-fargate-profile-private" + "kubernetes.io/cluster/%[1]s" = "shared" + } +} + +resource "aws_eip" "private" { + count = 2 + depends_on = [aws_internet_gateway.test] + + vpc = true +} + +resource "aws_nat_gateway" "private" { + count = 2 + + allocation_id = aws_eip.private[count.index].id + subnet_id = aws_subnet.private[count.index].id +} + +resource "aws_route_table" "private" { + count = 2 + + vpc_id = aws_vpc.test.id + + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.private[count.index].id + } +} + +resource "aws_route_table_association" "private" { + count = 2 + + subnet_id = aws_subnet.private[count.index].id + route_table_id = aws_route_table.private[count.index].id +} + +resource "aws_subnet" "public" { + count = 2 + + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, count.index) + vpc_id = aws_vpc.test.id + + tags = { + Name = "tf-acc-test-eks-fargate-profile-public" + "kubernetes.io/cluster/%[1]s" = "shared" + } +} + +resource "aws_eks_cluster" "test" { + name = %[1]q + role_arn = aws_iam_role.cluster.arn + + vpc_config { + subnet_ids = aws_subnet.public[*].id + } + + depends_on = [ + aws_iam_role_policy_attachment.cluster-AmazonEKSClusterPolicy, + aws_iam_role_policy_attachment.cluster-AmazonEKSServicePolicy, + aws_main_route_table_association.test, + ] +} +`, rName) +} + +func testAccAWSEksFargateProfileConfigFargateProfileName(rName string) string { + return testAccAWSEksFargateProfileConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_fargate_profile" "test" { + cluster_name = aws_eks_cluster.test.name + fargate_profile_name = %[1]q + pod_execution_role_arn = aws_iam_role.pod.arn + subnet_ids = aws_subnet.private[*].id + + selector { + namespace = "test" + } + + depends_on = [ + aws_iam_role_policy_attachment.pod-AmazonEKSFargatePodExecutionRolePolicy, + aws_route_table_association.private, + ] +} +`, rName) +} + +func testAccAWSEksFargateProfileConfigSelectorLabels1(rName, labelKey1, labelValue1 string) string { + return testAccAWSEksFargateProfileConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_fargate_profile" "test" { + cluster_name = aws_eks_cluster.test.name + fargate_profile_name = %[1]q + pod_execution_role_arn = aws_iam_role.pod.arn + subnet_ids = aws_subnet.private[*].id + + selector { + labels = { + %[2]q = %[3]q + } + namespace = "test" + } + + depends_on = [ + aws_iam_role_policy_attachment.pod-AmazonEKSFargatePodExecutionRolePolicy, + aws_route_table_association.private, + ] +} +`, rName, labelKey1, labelValue1) +} + +func testAccAWSEksFargateProfileConfigTags1(rName, tagKey1, tagValue1 string) string { + return testAccAWSEksFargateProfileConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_fargate_profile" "test" { + cluster_name = aws_eks_cluster.test.name + fargate_profile_name = %[1]q + pod_execution_role_arn = aws_iam_role.pod.arn + subnet_ids = aws_subnet.private[*].id + + selector { + namespace = "test" + } + + tags = { + %[2]q = %[3]q + } + + depends_on = [ + aws_iam_role_policy_attachment.pod-AmazonEKSFargatePodExecutionRolePolicy, + aws_route_table_association.private, + ] +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSEksFargateProfileConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return testAccAWSEksFargateProfileConfigBase(rName) + fmt.Sprintf(` +resource "aws_eks_fargate_profile" "test" { + cluster_name = aws_eks_cluster.test.name + fargate_profile_name = %[1]q + pod_execution_role_arn = aws_iam_role.pod.arn + subnet_ids = aws_subnet.private[*].id + + selector { + namespace = "test" + } + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } + + depends_on = [ + aws_iam_role_policy_attachment.pod-AmazonEKSFargatePodExecutionRolePolicy, + aws_route_table_association.private, + ] +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/website/aws.erb b/website/aws.erb index 70fdf5dce6b..b5b518a1cc5 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1235,6 +1235,9 @@
  • aws_eks_cluster
  • +
  • + aws_eks_fargate_profile +
  • aws_eks_node_group
  • diff --git a/website/docs/r/eks_fargate_profile.html.markdown b/website/docs/r/eks_fargate_profile.html.markdown new file mode 100644 index 00000000000..98a6dde4fe8 --- /dev/null +++ b/website/docs/r/eks_fargate_profile.html.markdown @@ -0,0 +1,97 @@ +--- +subcategory: "EKS" +layout: "aws" +page_title: "AWS: aws_eks_fargate_profile" +description: |- + Manages an EKS Fargate Profile +--- + +# Resource: aws_eks_fargate_profile + +Manages an EKS Fargate Profile. + +## Example Usage + +```hcl +resource "aws_eks_fargate_profile" "example" { + cluster_name = aws_eks_cluster.example.name + fargate_profile_name = "example" + pod_execution_role_arn = aws_iam_role.example.arn + subnet_ids = aws_subnet.example[*].id + + selector { + namespace = "example" + } +} +``` + +### Example IAM Role for EKS Fargate Profile + +```hcl +resource "aws_iam_role" "example" { + name = "eks-fargate-profile-example" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "eks-fargate-pods.amazonaws.com" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_iam_role_policy_attachment" "example-AmazonEKSFargatePodExecutionRolePolicy" { + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy" + role = aws_iam_role.example.name +} +``` + +## Argument Reference + +The following arguments are required: + +* `cluster_name` – (Required) Name of the EKS Cluster. +* `fargate_profile_name` – (Required) Name of the EKS Fargate Profile. +* `pod_execution_role_arn` – (Required) Amazon Resource Name (ARN) of the IAM Role that provides permissions for the EKS Fargate Profile. +* `selector` - (Required) Configuration block(s) for selecting Kubernetes Pods to execute with this EKS Fargate Profile. Detailed below. +* `subnet_ids` – (Required) Identifiers of private EC2 Subnets to associate with the EKS Fargate Profile. These subnets must have the following resource tag: `kubernetes.io/cluster/CLUSTER_NAME` (where `CLUSTER_NAME` is replaced with the name of the EKS Cluster). + +The following arguments are optional: + +* `tags` - (Optional) Key-value mapping of resource tags. + +### selector Configuration Block + +The following arguments are required: + +* `namespace` - (Required) Kubernetes namespace for selection. + +The following arguments are optional: + +* `labels` - (Optional) Key-value mapping of Kubernetes labels for selection. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - Amazon Resource Name (ARN) of the EKS Fargate Profile. +* `id` - EKS Cluster name and EKS Fargate Profile name separated by a colon (`:`). +* `status` - Status of the EKS Fargate Profile. + +## Timeouts + +`aws_eks_fargate_profile` provides the following [Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +* `create` - (Default `10 minutes`) How long to wait for the EKS Fargate Profile to be created. +* `delete` - (Default `10 minutes`) How long to wait for the EKS Fargate Profile to be deleted. + +## Import + +EKS Fargate Profiles can be imported using the `cluster_name` and `fargate_profile_name` separated by a colon (`:`), e.g. + +``` +$ terraform import aws_eks_fargate_profile.my_fargate_profile my_cluster:my_fargate_profile +```