Skip to content

Commit d4625cf

Browse files
committed
Added support to the rotate secrets module to handle updating a SVM password; Allowing the secret to be in a different region than the default; Used friendly variables names.
1 parent 01bfcd6 commit d4625cf

File tree

16 files changed

+303
-137
lines changed

16 files changed

+303
-137
lines changed

EKS/FSxN-as-PVC-for-EKS/terraform/eks-cluster.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ resource "aws_iam_policy" "trident_policy" {
8181
{
8282
"Action": "secretsmanager:GetSecretValue",
8383
"Effect": "Allow",
84-
"Resource": aws_secretsmanager_secret_version.fsx_secret_password.arn
84+
"Resource": module.svm_rotate_secret.secret_arn
8585
}
8686
],
8787
})
Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,40 @@
11
#
2-
# Generate a random password for FSx
3-
resource "random_string" "fsx_password" {
4-
length = 8
5-
min_lower = 1
6-
min_numeric = 1
7-
min_special = 0
8-
min_upper = 1
9-
numeric = true
10-
special = true
11-
override_special = "@$%^&*()_+="
12-
}
13-
14-
provider "aws" {
15-
alias = "secrets_provider"
16-
region = var.aws_secrets_region
17-
}
18-
#
19-
# Store the password in AWS Secrets Manager
20-
resource "aws_secretsmanager_secret" "fsx_secret_password" {
21-
provider = aws.secrets_provider
22-
name = "${var.fsx_password_secret_name}-${random_id.id.hex}"
23-
}
24-
resource "aws_secretsmanager_secret_version" "fsx_secret_password" {
25-
provider = aws.secrets_provider
26-
secret_id = aws_secretsmanager_secret.fsx_secret_password.id
27-
secret_string = jsonencode({username = "vsadmin", password = random_string.fsx_password.result})
2+
# Instantiate an AWS secret for the FSx ONTAP file system. It will set the initial password for the file system.
3+
module "fsxn_rotate_secret" {
4+
source = "../DevelopersAdocacy/FSx-ONTAP-samples-scripts/Management-Utilities/fsxn-rotate-secret/terraform"
5+
fsx_region = var.aws_region
6+
secret_region = var.aws_secrets_region
7+
aws_account_id = var.aws_account_id
8+
secret_name_prefix = var.secret_name_prefix
9+
fsx_id = aws_fsx_ontap_file_system.eksfs.id
2810
}
2911
#
30-
# Note that this allows traffic from both the private and public subnets. However
31-
# the security groups only allow traffic from the public subnet over port 22 when
32-
# the source has the jump server SG assigned to it. So, basically, it only allows traffic
33-
# from the jump server from the public subnet.
12+
# Create a FSxN file system.
3413
resource "aws_fsx_ontap_file_system" "eksfs" {
3514
storage_capacity = var.fsxn_storage_capacity
3615
subnet_ids = module.vpc.private_subnets
3716
deployment_type = "MULTI_AZ_1"
3817
throughput_capacity = var.fsxn_throughput_capacity
3918
preferred_subnet_id = module.vpc.private_subnets[0]
4019
security_group_ids = [aws_security_group.fsx_sg.id]
41-
fsx_admin_password = random_string.fsx_password.result
42-
route_table_ids = concat(module.vpc.private_route_table_ids, module.vpc.public_route_table_ids)
20+
route_table_ids = concat(module.vpc.private_route_table_ids, module.vpc.public_route_table_ids)
4321
tags = {
4422
Name = var.fsx_name
4523
}
4624
}
4725
#
26+
# Instantiate an AWS secret for the storage virtual machine. It will set the initial password for the SVM.
27+
module "svm_rotate_secret" {
28+
source = "../DevelopersAdocacy/FSx-ONTAP-samples-scripts/Management-Utilities/fsxn-rotate-secret/terraform"
29+
fsx_region = var.aws_region
30+
secret_region = var.aws_secrets_region
31+
aws_account_id = var.aws_account_id
32+
secret_name_prefix = var.secret_name_prefix
33+
svm_id = aws_fsx_ontap_storage_virtual_machine.ekssvm.id
34+
}
35+
#
4836
# Create a vserver and assign the 'vsadmin' the same password as fsxadmin.
4937
resource "aws_fsx_ontap_storage_virtual_machine" "ekssvm" {
5038
file_system_id = aws_fsx_ontap_file_system.eksfs.id
5139
name = "ekssvm"
52-
svm_admin_password = random_string.fsx_password.result
5340
}

EKS/FSxN-as-PVC-for-EKS/terraform/outputs.tf

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
output "region" {
2-
description = "AWS region"
32
value = var.aws_region
43
}
54

65
output "fsx-password-secret-name" {
7-
value = aws_secretsmanager_secret.fsx_secret_password.name
6+
value = module.fsxn_rotate_secret.secret_name
87
}
98

109
output "fsx-password-secret-arn" {
11-
value = aws_secretsmanager_secret_version.fsx_secret_password.arn
10+
value = module.fsxn_rotate_secret.secret_arn
11+
}
12+
13+
output "svm-password-secret-name" {
14+
value = module.svm_rotate_secret.secret_name
15+
}
16+
17+
output "svm-password-secret-arn" {
18+
value = module.svm_rotate_secret.secret_arn
1219
}
1320

1421
output "fsx-svm-name" {
Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,42 @@
11
variable "aws_region" {
2-
default = "us-west-2"
3-
description = "aws region where you want the resources deployed."
2+
description = "The AWS region where you want the resources deployed."
3+
type = string
44
}
55

66
variable "aws_secrets_region" {
7-
default = "us-west-2"
8-
description = "The region where you want the FSxN secret stored within AWS Secrets Manager."
7+
description = "The AWS region where you want the FSxN and SVM secrets stored within AWS Secrets Manager."
8+
type = string
9+
}
10+
11+
variable "aws_account_id" {
12+
description = "The AWS account ID. Used to create very specific permissions in the IAM role for the EKS cluster."
13+
type = string
914
}
1015

1116
variable "fsx_name" {
12-
default = "eksfs"
1317
description = "The name you want assigned to the FSxN file system."
18+
default = "eksfs"
1419
}
1520

16-
variable "fsx_password_secret_name" {
21+
variable "secret_name_prefix" {
22+
description = "The base name of the secrets (FSxN and SVM) to create within the AWS Secrets Manager. A random string will be appended to the end of the secreate name to ensure no name conflict."
1723
default = "fsx-eks-secret"
18-
description = "The base name of the secret to create within the AWS Secrets Manager that will contain the FSxN password. A random string will be appended to the end of the secreate name to ensure no name conflict."
1924
}
2025

2126
variable "fsxn_storage_capacity" {
22-
default = 1024
2327
description = "The storage capacity, in GiBs, to be allocated to the FSxN clsuter. Must be at least 1024, and less than 196608."
28+
type = number
29+
default = 1024
2430
validation {
2531
condition = var.fsxn_storage_capacity >= 1024 && var.fsxn_storage_capacity < 196608
2632
error_message = "The storage capacity must be at least 1024, and less than 196608."
2733
}
2834
}
2935

3036
variable "fsxn_throughput_capacity" {
31-
default = 128
3237
description = "The throughput capacity to be allocated to the FSxN cluster. Must be 128, 256, 512, 1024, 2048, 4096."
38+
type = string # Set to a string so it can be used in a "contains()" function.
39+
default = 128
3340
validation {
3441
condition = contains([128, 256, 512, 1024, 2048, 4096], var.fsxn_throughput_capacity)
3542
error_message = "The throughput capacity must be 128, 256, 512, 1024, 2048, or 4096."
@@ -38,34 +45,38 @@ variable "fsxn_throughput_capacity" {
3845
#
3946
# Keep in mind that key pairs are regional, so pick one that is in the region specified above.
4047
variable "key_pair_name" {
41-
default = "MUST REPLACE WITH YOUR KEY PAIR NAME"
4248
description = "The key pair to associate with the jump server."
49+
default = "MUST REPLACE WITH YOUR KEY PAIR NAME"
50+
type = string
4351
validation {
4452
condition = var.key_pair_name != "MUST REPLACE WITH YOUR KEY PAIR NAME"
4553
error_message = "You must specify a key pair name."
4654
}
4755
}
4856

4957
variable "secure_ips" {
50-
default = ["0.0.0.0/0"]
5158
description = "List of CIDRs that are allowed to ssh into the jump server."
59+
default = ["0.0.0.0/0"]
5260
}
5361

5462
################################################################################
5563
# Don't change any variables below this line.
5664
################################################################################
5765

5866
variable "trident_version" {
59-
default = "v24.2.0-eksbuild.1"
6067
description = "The version of Astra Trident to 'add-on' to the EKS cluster."
68+
default = "v24.2.0-eksbuild.1"
69+
type = string
6170
}
6271

6372
variable "kubernetes_version" {
64-
default = 1.29
6573
description = "kubernetes version"
74+
default = 1.29
75+
type = string
6676
}
6777

6878
variable "vpc_cidr" {
69-
default = "10.0.0.0/16"
7079
description = "default CIDR range of the VPC"
80+
default = "10.0.0.0/16"
81+
type = string
7182
}

Management-Utilities/fsxn-rotate-secret/README.md

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Rotate FSxN File System Passwords
22

33
## Introduction
4-
This sample provides a way to rotate a Secrets Manager secret that is used to manage FSxN file system.
5-
It is a Lambda function that is expected to be triggered by the Secrets Manager rotation feature.
4+
This sample provides a way to rotate a Secrets Manager secret that is used to hold the
5+
password assigned to an FSxN file system or a FSxN Storage Virtual Machine.
6+
It is a Lambda function that is expected to be invoked by the Secrets Manager rotation feature.
67
The Secrets Manager should invoke the function four times, each time with the `stage` field, in the `event` dictionary passed in, set to one of the following values:
78

89
| Stage | Description |
@@ -28,6 +29,7 @@ relationship with the AWS Lambda service.
2829
| secretsManager:DescribeSecret | \<secretARN> | \<secretARN> is the AWS ARN of the secret to rotate. |
2930
| secretsmanager:GetRandomPassword | \* | The scope doesn't matter, since this function doesn't have anything to do with any AWS resources. |
3031
| fsx:UpdateFileSystem | \<fileSystemARN> | \<fileSystemARN> is the AWS ARN of the FSxN file system to manage. |
32+
| fsx:UpdateStorageVirtualMachine | \<svmARN> | \<svmARN> is the AWS ARN of the Storage Virtual Machine to manage. |
3133
| logs:CreateLogGroup | arn:aws:logs:\<region>:\<accountID>:\* | This allows the Lambda function to create a log group in CloudWatch. This is optional but allows you to get diagnostic information from the Lambda function. |
3234
| logs:CreateLogStream | arn:aws:logs:\<region>:\<accountID>:log-group:/aws/lambda/\<Lambda_function_name>:\* | This allows the Lambda function to create a log stream in CloudWatch. This is optional but allows you to get diagnostic information from the function.|
3335
| logs:PutLogEvents | arn:aws:logs:\<region>:\<accountID>:log-group:/aws/lambda/\<Lambda_function_name>:\* | This allows the Lambda function to write log events to a log stream in CloudWatch. This is optional but allows you to get diagnostic information from the function.|
@@ -53,11 +55,25 @@ secretsmanager AWS service to invoke the Lambda function. Do that do the followi
5355
- The principal should already be set to `secretsmanager.amazonaws.com`
5456
- Set action to `lambda:InvokeFunction`
5557

56-
##### Step 2.4 - Enable Secrets Manager Rotation
57-
To enable rotation of the secret, you will need to go to the Secrets Manager console and
58-
select the secret you want to rotate. Click on the "Edit rotation" button and select the
59-
Lambda function you created above. You can also set the rotation schedule to whatever you
60-
want. The default is 30 days.
58+
#### Step 3 - Enable Secrets Manager Rotation
59+
To enable the rotation of the secret, go will need to the Secrets Manager page of the AWS console
60+
and click on the secret you want to rotate, then:
61+
##### Step 3.1 - Set the tags
62+
The way Lambda function knows which FSxN file system, or which SVM, to update the password on is
63+
via the tags associated with the secret. The following are the tags that the program looks for:
64+
|Tag Key|Tag Value|Description|
65+
|:------|:--------|:----------|
66+
|region|\<region\>|The region the FSxN file system resides in.|
67+
|fsx_id|\<file-System-id\>|The FSxN file system id.|
68+
|svm_id|\<svm-id\>|The Storage Virtual Machine id.|
69+
Note that the Lambda function can only manage one password, so either set the value for the `fsx_id` or the `svm_id` tag, both not both.
70+
:warning: **Warning:** If both the `fsx_id` and `svm_id` tags are set, the `svm_id` tag will be used and the fsx_id will be silently ignored.
71+
72+
##### Step 3.2 - Enable rotation feature
73+
Click on the Rotation tab and then click on the "Edit rotation" button. That should bring up a
74+
pop-up window. Click on the "Automatic rotation" slider to enable the feature and then configure
75+
the rotation schedule you want. The last step is to
76+
select the rotation function that you created in the steps above and click on the "Save" button.
6177

6278
### Terraform Method
6379
The Terraform module provided in the `terraform` directory can be used to create the Secrets Manager
@@ -128,13 +144,43 @@ Make sure to replace all values within `< >` with your own variables.
128144
module "fsxn_rotate_secret" {
129145
source = "github.com/NetApp/FSx-ONTAP-samples-scripts/Management-Utilities/fsxn-rotate-secret/terraform"
130146
131-
region = <region>
132-
awsAccountId = <aws_account_id>
133-
fsxId = <fsx_id>
147+
fsx_region = <region> # The region the FSxN file system resides in.
148+
secret_region = <region> # The region the secret resides in.
149+
aws_account_id = <aws_account_id> # The AWS account id that the FSxN file system resides in.
150+
fsx_id = <fsx_id>
151+
svm_id = <svm_id>
134152
secretNamePrefix = "fsx_admin_secret"
153+
rotationFrequency = "rate(30 days)"
135154
}
136155
```
137-
That's all there is to it!
156+
Note that the Lambda function can only manage one password, so either set the value for the `fsxId` or the `svmId` tag, both not both.
157+
:warning: **Warning:** If both the `fsxId` and `svmId` tags are set, the `svmId` tag will be used and the fsxId will be silently ignored.
158+
159+
At this point, you can run `terraform init` and `terraform apply` to create the secret that will automatically rotate
160+
the password for the FSxN file system or SVM.
161+
162+
#### Inputs
163+
The following are the inputs for the module:
164+
| Name | Description | Type | Default | Required |
165+
|------|-------------|------|---------|:--------:|
166+
| fsx_region | The region where the FSxN file system resides in. | string | | yes |
167+
| secret_region | The region where the secret will resides in. | string | | yes |
168+
| aws_account_id | The AWS account id that the FSxN file system resides in. Used to create roles with least privilege. | string |\*| no |
169+
| fsx_id | The FSxN file system id. Note that either fsxId or svmId must be provided, but not both | string | | no |
170+
| svm_id | The Storage Virtual Machine id. Note that either fsxId or svmId must be provided, but not both | string | | no |
171+
| secret_name_prefix | The prefix to use for the secret name. | string | fsxn-secret | no |
172+
| rotation_frequency | The frequency to rotate the password in AWS's "rate" or "cron" notation. | string | rate(30 days) | yes |
173+
174+
#### Outputs
175+
The following are the outputs for the module:
176+
| Name | Description |
177+
|------|-------------|
178+
| secret_arn | The ARN of the secret created. |
179+
| secret_name | The name of the secret created. |
180+
| lambda_arn | The ARN of the Lambda function created. |
181+
| lambda_name | The name of the Lambda function created. |
182+
| role_arn | The ARN of the IAM role created. |
183+
| role_name | The name of the IAM role created. |
138184

139185
## Author Information
140186

Management-Utilities/fsxn-rotate-secret/fsxn_rotate_secret.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
charactersToExcludeInPassword = '/"\'\\'
1414

1515
################################################################################
16-
# THis function is used to get the value of a tag from a list of tags.
16+
# This function is used to get the value of a tag from a list of tags.
1717
################################################################################
1818
def getTagValue(tags, key):
1919
for tag in tags:
@@ -67,20 +67,27 @@ def set_secret(secretsClient, arn, token):
6767

6868
password = secretValueResponse['SecretString']
6969
#
70-
# Get the FSx file system ID and region from the secret's tags.
70+
# Get the FSx file system ID, SVM ID and region from the secret's tags.
7171
secretMetadata = secretsClient.describe_secret(SecretId=arn)
7272
tags = secretMetadata['Tags']
73-
fsxId = getTagValue(tags, 'fsxId')
73+
fsxId = getTagValue(tags, 'fsx_id')
7474
fsxRegion = getTagValue(tags, 'region')
75-
if fsxId is None or fsxRegion is None:
76-
message=f"Unable to retrieve the FSx file system ID ('fsxId') or region ('region') tags from secret {arn}."
75+
svmId = getTagValue(tags, 'svm_id')
76+
logging.info(f"fsxId={fsxId}, svmId={svmId}, fsxRegion={fsxRegion}")
77+
78+
if (fsxId is None and svmId is None) or fsxRegion is None:
79+
message=f"Error, tags 'fsxId' or 'svmId' and the 'region' have to be set on the secret's ({arn}) resource."
7780
logger.error(message)
7881
raise Exception(message) # Signal to the Secrets Manager that the rotation failed.
7982
#
80-
# Update the FSx file system with the new password.
83+
# Update the FSx file system, or SVM, with the new password.
8184
fsxClient = boto3.client(service_name='fsx', region_name=fsxRegion)
82-
fsxClient.update_file_system(OntapConfiguration={"FsxAdminPassword": password}, FileSystemId=fsxId)
83-
logger.info(f"Successfully set the FSxN ({fsxId}) password to secret stored in {arn} with a VersionStage = 'AWSPENDING'.")
85+
if svmId is None or svmId == "":
86+
fsxClient.update_file_system(OntapConfiguration={"FsxAdminPassword": password}, FileSystemId=fsxId)
87+
logger.info(f"Successfully set the FSxN ({fsxId}) password to secret stored in {arn} with a VersionStage = 'AWSPENDING'.")
88+
else:
89+
fsxClient.update_storage_virtual_machine(StorageVirtualMachineId=svmId, SvmAdminPassword=password)
90+
logger.info(f"Successfully set the SVM ({svmId}) password to secret stored in {arn} with a VersionStage = 'AWSPENDING'.")
8491

8592
################################################################################
8693
# Usually this function would be used to test that the service has been updated
@@ -142,7 +149,7 @@ def lambda_handler(event, context):
142149
# Set the logging level higher for these noisy modules to mute thier messages.
143150
logging.getLogger("boto3").setLevel(logging.WARNING)
144151
logging.getLogger("botocore").setLevel(logging.WARNING)
145-
152+
146153
logger.info(f'arn={arn}, token={token}, step={step}.')
147154
#
148155
# Create a client to the secrets manager service.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../fsxn_rotate_secret.py

0 commit comments

Comments
 (0)