Using AWS Lambda to Automate Tasks for Pure Cloud Block Store
This document walks through the steps of how to use AWS Lambda to invoke scripts and execute them against Pure Cloud Block Store on AWS.
Introduction
AWS Lambda is a serverless compute service that allows you to run code without the need for provisioning or managing servers. It allows you to package and upload your code along with its dependencies, and it can be run directly in the GUI code editor, using AWS CLI and API, or it can be trigger using subscribed events.
This guide objective is to create a Lambda that will serve as a skeleton function that can be reused for any use cases and integration points. It can be applied to achieve a certain task or multitasks on Cloud Block Store, and the function can be triggered automatically with event-driven approach or by scheduled events.
The scripting method utilized in this guide to access and interact with CBS is by using Pure Python client SDK (refer to the latest SDK documentation here). This Python package provides clients that use the Pure1, FlashArray, and FlashBlade REST APIs. Therefore, If you have already created scripts using Pure Python SDK, it can be reused here using this approach.
To name few of the use cases that can be automated with AWS Lambda:
- Creating new EC2 instance and attaching volumes from CBS.
- Expanding volumes when EC2 storage utilization hit a certain metric.
- Cleaning up the provisioned storage for Auto Scaling EC2 scale in actions
- Replicating protection groups to second array
- Collecting performance metrics
Solution Workflow
Setting up the Lambda function requires a couple of steps, each are explained in details in the sections below:
- Creat API Client
- Provide IAM permissions
- Package dependences to Lambda Layer
- Configure Lambda function
- Run Lambda Function
Note: This guide provides instructions using AWS Console and AWS CLI. Alternatively, you can use Terraform by downloading the manifest files from this Github and update your environment variables in this file terraform.tfvars.
Note: CLI instructions in this guide presumes you have installed AWS CLI on your local machine and configured/authenticated with your AWS account.
Note: Another option for CLI instructions is to use AWS CloudShell, which can be accessed in AWS console.
Create API Client
In order to use Pure Python SDK, FlashArray/Cloud Block Store uses API Client method to authenticate to the array for REST access. The following steps produce the required parameters, which will be used in the Lambda function code later on.
Generating a RSA key pair
If you don't already have an RSA key pair available, generate one using the Linux commands below:
openssl genrsa -out cbsprivate.pem 2048 openssl rsa -in cbsprivate.pem -outform PEM -pubout -out cbspublic.pem
Display your new public key in plain text using the following command:
cat cbspublic.pem
You should see something similar to the following:
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt7WqkyADURjdO9lPtNXW 4ihQ4FF/DDsSslJD7N9b8j5ghD5BJO543L2Nr96rh8Fa9pXpbSbiMGG67RQ695wK uxrTdyhKQ8lZQuCEzX7+sUoNopeRK7vfsmiv+eT6g/wEFg4KBaTIrYKqPfEVw9Ub 0Ib1CjXHlx+DZdmff47ZrhOwaGQ4oYsJEKAA0Yc608b2yD9H84UN/uq/Ukh5Q7Th 3BtzY6LcQe5FrktQomH8AFCvzY7XBUao8iCPmg7jLnaFZmQpipslwoUpRfxDDD5L 7rO1OjR1GX2+3PInKYQ+ROoMp8MPUKCUU/pH4BDFAZF7A9W6H48bD2mnSjUnqwIp gQIDAQAB -----END PUBLIC KEY-----
Copy the public key plain text into your clipboard or in a separate document for future reference, and you will also need it in the next step.
Creating an API Client
To create an API client, SSH into your Cloud Block Store as a user with array admin privileges and enter the following command:
pureapiclient create --max-role array_admin --public-key <name_of_your_app>
Note: Each API Client has an Issuer
value. The issuer for an API Client is the IdP that is associated with that API Client. The issuer defaults to the name of the API Client, but you can optionally user the --issuer
argument to set it to a different value. In this guide, you will always be acting as the IdP (even though you might delegate the actual token creation to a script), so feel free to set the issuer to whatever you want, or leave it as the default.
Note: valid values for the --max-role
parameter are: array_admin
, storage_admin
, ops_admin
and readonly
.
Note: you can also specify the optional --access-token-ttl
parameter, which specifies the time interval in which the issued access tokens are valid. Allowed values are between1000
(1 second) to 86400000
(1 day). The default value is 1 day.
When you press 'Enter' after typing the command above, you get prompted to enter your public key:
pureapiclient create --max-role array_admin --public-key myClient Please enter public key followed by ^D:
Paste the public key plain text you retrieved with the cat fa2xpublic.pem
command above and press Enter. Then press Control+D.
Note: Only after pressing Enter should you press Control+D, otherwise, this won't work.
You should see an output similar to the following:
Name Enabled Max Role Issuer Access Token TTL Client ID Key ID myClient False array_admin myClient 1d ab18a763-2b34-4b61-aa8e-a45afc7ad945 e7d175d3-c88b-41ef-b5f9-79c75a53cd2e
As you may notice above, your API client is disabled by default, so you must enable it with the following command:
pureapiclient enable myClient Name Enabled Role Issuer Access Token TTL Client ID Key ID myClient True array_admin myClient 1d ab18a763-2b34-4b61-aa8e-a45afc7ad945 e7d175d3-c88b-41ef-b5f9-79c75a53cd2e
Copy the details of your API Client in a separate document which will be paste to the python code later.
Store Private Key in AWS System Manager Parameter Store
The private key generated in the previous step will be used by the Lambda function. Therefore, storing it in SSM Paramete Store helps retrieving it upon request securely and programmatically using AWS python SDK.
In your local machine, run the below command after changing to the directory where the private key is located.
aws ssm put-parameter --name "/cbs/apiclient/privatekey" --value "$(cat cbsprivate.pem)" --type String
Provide IAM permissions
Lambda function is using IAM permissions to access and interact with AWS service. Therefore, we need to assign an execution role during the function creation that contains the required IAM polices.
Create IAM Policy
First, follow this steps to create IAM policy using AWS console.
- In the navigation pane on the left side of the AWS console, choose Policies.
- Click Create policy, then select JSON tab.
- Copy/Paste the below JSON formatted text below. Note: Replace the Resource (colored in green) by your parameter ARN.
- Review the policy and give it a name and a description, then Create policy.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
"Resource": "*",
"Effect": "Allow"
},
{
"Effect": "Allow",
"Action": "ssm:GetParameter",
"Resource": "arn:aws:ssm:eu-central-1:xxxxxxxxxxx:parameter/cbs/apiclient/privatekey"
}
]
}
Note: For ssm: GetParameter policy, It is Recommended specify only the resource "parameter" giving access to. To get the ARN for the resource run the below command:
aws ssm get-parameter --name "cbs"
Expected output:
{ "Parameter": { "Name": "cbs", "Type": "String", "Value": "xxxxxxxxxx", "Version": 1, "LastModifiedDate": "2022-01-06T14:31:31.039000+01:00", "ARN": "arn:aws:ssm:eu-central-1:xxxxxxxxxxx:parameter/cbs/apiclient/privatekey", "DataType": "text" } }
Create IAM Role
Once you have an IAM Policy, the next step is to create the execution role, which is carries these permissions and will be later assigned to the new created function.
- In the navigation pane on the left side of the IAM console, choose Roles, and then choose Create role.
- For Select type of trusted entity, pick AWS service.
- Under Attach permission policy, two policies to be attached:
- Search the newly created policy and select.
- Search for AWSLambdaVPCAccessExecutionRole, this is an AWS managed policy, and it has all the permission needed to connect the Lambda function to a private VPC (The VPC which CBS resides).
- Review the role and give it a name and a description, then Create role.
Note: The sample IAM policy above is just an example and it gives permissions only to CloudWatch logs and System Manager Parameter Store, you can customize this policy later when adding triggers or requiring the function to access certain services or resources.
Package Dependences to Lambda Layer
Even though python runtime in Lambda comes with its own set of standard libraries, such as boto3, os, urllib3, etc ..., those libraries can be imported and used in your function. However, the troublesome arose when you want to use a specific library or libraries that are non-native python packages and not available with the default libraries that Lambda offers. For this guide case, py-pure-client library.
AWS solves this with different approaches, for the sake of the reproducibility of the functions, Lambda layers has been chosen. Lambda Layer allows you to package your function code dependencies and third-party libraries in a .zip file and upload it as Lambda layer. Every function can reference up to 5 layers, and they can be shared publicly or with a specific AWS account. Therefore, this is what we will wrap the py-pure-client package into and then share it with the function.
Note: Lambda uses Amazon Linux environment as its OS. Therefore, packages installed on Windows may not work.
Since packaging might go wrong for a couple of reason (third-party libraries missing or mismatch in the dependencies). You can use the pre-packaged zip file uploaded to S3.
https://pure-storage-kb-solutions.s3.eu-central-1.amazonaws.com/pure-python-lambda-layer/py-pure-client-runtime3.6.zip
Create Lambda Layer
The Lambda layer created using the provided package, contains only the py-pure-client. if you want to add other libraries to your function, you can create up to 5 layers. Instructions on how to package library dependencies can be found here.
AWS Console
Navigate to AWS Lambda and choose Layers from the left side pane, then click Create layer.
Under Create layer choose the following:
- Enter layer name.
- Enter description.
- Select upload a file from Amazon S3, then Paste the S3 link URL provided above.
- Choose x86_64 for the architecture.
- Choose Python 3.6 for the runtime.
Click Create, and with this a layer has been created. The next step is to create a function and attach the layer to it.
AWS CLI
In your local machine or AWS CloudShell, run the below command. You can change the layer-name and description (the parameters colored in green). DO NOT change the S3 content or compatibility-runtime.
aws lambda publish-layer-version \ --layer-name my-layer \ --description "My Python layer" \ --content S3Bucket=pure-storage-kb-solutions,S3Key=pure-python-lambda-layer/py-pure-client-runtime3.6.zip \ --compatible-runtimes python3.6
From the output JSON response, copy the LayerVersionArn
to a separate document, so it can be added to the Lambda function later using the AWS CLI.
Output response example:
{ "Content": { "Location": "https://awslambda-us-east-2-layers.s3.us-east-2.amazonaws.com/snapshots/123456789012/my-layer-4aaa2fbb-ff77-4b0a-ad92-5b78a716a96a?versionId=27iWyA73cCAYqyH...", "CodeSha256": "tv9jJO+rPbXUUXuRKi7CwHzKtLDkDRJLB3cC3Z/ouXo=", "CodeSize": 169 }, "LayerArn": "arn:aws:lambda:us-east-2:123456789012:layer:my-layer", "LayerVersionArn": "arn:aws:lambda:us-east-2:123456789012:layer:my-layer:1", "Description": "My layer", "CreatedDate": "2018-11-14T23:03:52.894+0000", "Version": 1, "CompatibleArchitectures": [ "arm64", "x86_64" ], "CompatibleRuntimes": [ "python3.6" ] }
Configure Lambda Function
Lambda function can created and updated by either using the Lambda console or Lambda API via AWS CLI. This section shows both approaches and provides sample code.
Create Lambda Function
AWS Console
Navigate to AWS Lambda and choose Functions from the left side pane, then click Create function.
Under Create function choose the following:
- Select Author from scratch.
- Enter Function name.
- Choose Python 3.6 for the runtime.
- Choose x86_64 for the architecture.
- Under Permission, select Use an existing role, then from the dropdown choose the IAM role created in previous steps.
Click Create function, and with this a function has been created, next step is to configure the function networking and edit the code.
AWS CLI
Run the below command by replacing with your function name and IAM role ARN.
aws lambda create-function --function-name my-function \ --runtime python3.6 \ --architecture x86_64 \ --role arn:aws:iam::123456789012:role/cbs-lambda-role --runtime 60 --memory-size 256
Configure Networking
By default, Lambda functions runs in an isolated VPC managed by AWS. In order for the function to communicate with Cloud Block Store, you need to edit VPC configuration of your function by connecting it to the same VPC and subnet where your CBS management interface located, or to any VPC/subnet has routing to CBS. The steps to achieve this connection are the following:
AWS Console
In the function page, click on Configuration, then select VPC, then click Edit.
Enter the VPC, subnet, and security group. Then click Save.
AWS CLI
Run the below command to configure the VPC network for the function. Replace with your function name, subnet ID, and security group ID.
aws lambda update-function-configuration --function-name pure-cbs-function \ --vpc-config SubnetIds=subnet-0xxxxxxxxxxx,SecurityGroupIds=sg-0xxxxxxxxxxxxx
Upload Sample Code
AWS Console
Copy the sample code from the github link below, then paste the code into the code-editor, then click Deploy.
https://github.com/PureStorage-OpenConnect/cloudblockstore-scripts/tree/main/aws/aws-lambda-python/sample-lambda-code
AWS CLI
First, package your code file .py in a zip file then run lambda update function command below.
where:
cbs-function-lambda.zip
is the name of the zip file
lambda_fucntion.py
is the python file
zip cbs-function-lambda.zip lambda_function.py
aws lambda update-function-code --function-name pure-cbs-function --zip-file fileb://cbs-function-lambda.zip
Add Layer
The command below adds the previously created layer to the function. Make sure to paste the layer ARN copied in previous step.
aws lambda update-function-configuration \ --function-name pure-cbs-function \ --layers arn:aws:lambda:xx-central-1:xxxxxxxx:layer:my-layer:1
Add Environment Variables
Lambda function can use environment variable that can be retrieved in the code, this will element the need to hard code variables and allow scripts to be reused. The command below adds the following as environment variables: Retrieve variables
CBS_IP: Array "CBS" management IP address
CBS_USERNAME: Array username with admin_access
CLIENT_ID: Client ID created in previous section Create Client API
KEY_ID: Key ID created in previous section Create Client API
CLIENT_API_ISSUER: Issuer created in previous section Create Client API
aws lambda update-function-configuration --function-name pure-cbs-function \
--environment "Variables={CBS_IP='x.x.x.x',CBS_USERNAME='pureuser',CLIENT_ID='xxxxxxxxxx',KEY_ID='xxxxxxxxx',CLIENT_API_ISSUER='myClient'}"
Run Lambda Function
Test Sample Script
Go to the created function, then click Test.
Test will take some time and then return with response, click on Execution results in the code-editor.
Monitor with CloudWatch logs
All Lambda invocations are automatically logged and stored with CloudWatch. Therefore, the logs generated by your code using logging statements can be found and accessed under Monitor tab or you can navigate to CloudWatch console.