Skip to content

Commit a973faa

Browse files
committed
Add Python IRSA example for managing AWS ECR repositories
Signed-off-by: Han Verstraete (OpenFaaS Ltd) <han@openfaas.com>
1 parent bbc17a1 commit a973faa

1 file changed

Lines changed: 181 additions & 0 deletions

File tree

docs/languages/python.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,187 @@ curl -X POST http://127.0.0.1:8080/function/s3-example?key=hello.txt \
586586
curl http://127.0.0.1:8080/function/s3-example
587587
```
588588

589+
## Example: Manage AWS ECR repositories with IRSA
590+
591+
This example is for OpenFaaS deployed on [AWS EKS](https://aws.amazon.com/eks/). It shows how to use the `boto3` SDK with [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) to manage AWS ECR repositories from a Python function. Instead of storing static AWS credentials as secrets, the function obtains ambient credentials automatically from a Kubernetes Service Account that is mapped to an IAM role.
592+
593+
IRSA is the recommended way to handle AWS credentials for functions running on EKS. It avoids long-lived static credentials that need to be manually rotated, and follows the principle of least privilege by scoping permissions to individual functions through IAM roles.
594+
595+
IRSA must be enabled on your EKS cluster. This requires an IAM OIDC identity provider associated with the cluster. See [Creating an IAM OIDC provider for your cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html) for setup instructions. For a more detailed end-to-end walkthrough, see: [Manage AWS Resources from OpenFaaS Functions With IRSA](https://www.openfaas.com/blog/irsa-functions/).
596+
597+
**1. Create an IAM Policy**
598+
599+
Create a policy that grants the permissions your function needs. This example creates and queries ECR repositories:
600+
601+
```json
602+
{
603+
"Version": "2012-10-17",
604+
"Statement": [
605+
{
606+
"Effect": "Allow",
607+
"Action": [
608+
"ecr:CreateRepository",
609+
"ecr:DeleteRepository",
610+
"ecr:DescribeRepositories"
611+
],
612+
"Resource": "*"
613+
}
614+
]
615+
}
616+
```
617+
618+
Save the above to `ecr-policy.json` and create the policy:
619+
620+
```bash
621+
aws iam create-policy \
622+
--policy-name ecr-create-query-repository \
623+
--policy-document file://ecr-policy.json
624+
```
625+
626+
Note the ARN from the output, e.g. `arn:aws:iam::ACCOUNT_NUMBER:policy/ecr-create-query-repository`.
627+
628+
**2. Create an IAM Role and Kubernetes Service Account**
629+
630+
Use `eksctl` to create a Kubernetes Service Account in the `openfaas-fn` namespace that is linked to an IAM role with the policy attached:
631+
632+
```bash
633+
export ARN=arn:aws:iam::ACCOUNT_NUMBER:policy/ecr-create-query-repository
634+
635+
eksctl create iamserviceaccount \
636+
--name openfaas-create-ecr-repo \
637+
--namespace openfaas-fn \
638+
--cluster <cluster-name> \
639+
--role-name ecr-create-query-repository \
640+
--attach-policy-arn $ARN \
641+
--region eu-west-1 \
642+
--approve
643+
```
644+
645+
This can also be done manually by creating the IAM Role in AWS, followed by a Kubernetes Service Account annotated with `eks.amazonaws.com/role-arn`.
646+
647+
**3. Create the function**
648+
649+
Pull the `python3-http-debian` template and scaffold a new function:
650+
651+
```bash
652+
faas-cli template store pull python3-http-debian
653+
faas-cli new --lang python3-http-debian ecr-create-repo \
654+
--prefix ttl.sh/openfaas-examples
655+
```
656+
657+
**4. Add the boto3 dependency**
658+
659+
Add `boto3` to the function's `requirements.txt`:
660+
661+
```
662+
boto3
663+
```
664+
665+
**5. Configure the function**
666+
667+
Update `stack.yaml` to set the AWS region and assign the Kubernetes Service Account created for IRSA. The `com.openfaas.serviceaccount` annotation tells OpenFaaS which service account to attach to the function's pod:
668+
669+
```yaml
670+
functions:
671+
ecr-create-repo:
672+
lang: python3-http-debian
673+
handler: ./ecr-create-repo
674+
image: ttl.sh/openfaas-examples/ecr-create-repo:latest
675+
annotations:
676+
com.openfaas.serviceaccount: openfaas-create-ecr-repo
677+
environment:
678+
AWS_REGION: eu-west-1
679+
```
680+
681+
No secrets are needed — the AWS SDK picks up credentials automatically from the service account token that is mounted into the pod by EKS.
682+
683+
**6. Write the handler**
684+
685+
The handler uses `boto3` to create ECR repositories. The `boto3` session is initialised once and reused across invocations. Because IRSA is configured, the SDK automatically obtains temporary credentials from the service account token without any explicit credential management.
686+
687+
```python
688+
import os
689+
import json
690+
import boto3
691+
692+
ecrClient = None
693+
694+
def initECR():
695+
session = boto3.Session(
696+
region_name=os.getenv('AWS_REGION'),
697+
)
698+
return session.client('ecr')
699+
700+
def handle(event, context):
701+
global ecrClient
702+
703+
if ecrClient is None:
704+
ecrClient = initECR()
705+
706+
if event.method != 'POST':
707+
return {
708+
"statusCode": 405,
709+
"body": "Method not allowed"
710+
}
711+
712+
body = json.loads(event.body)
713+
name = body.get('name')
714+
715+
if not name:
716+
return {
717+
"statusCode": 400,
718+
"body": "Missing in body: name"
719+
}
720+
721+
# Check if the repository already exists
722+
try:
723+
ecrClient.describe_repositories(repositoryNames=[name])
724+
return {
725+
"statusCode": 200,
726+
"body": json.dumps({"message": "Repository already exists"})
727+
}
728+
except ecrClient.exceptions.RepositoryNotFoundException:
729+
pass
730+
731+
# Create the repository
732+
response = ecrClient.create_repository(
733+
repositoryName=name,
734+
imageTagMutability='MUTABLE',
735+
encryptionConfiguration={
736+
'encryptionType': 'AES256',
737+
},
738+
imageScanningConfiguration={
739+
'scanOnPush': False,
740+
},
741+
)
742+
743+
return {
744+
"statusCode": 201,
745+
"body": json.dumps({
746+
"arn": response['repository']['repositoryArn']
747+
})
748+
}
749+
```
750+
751+
**7. Deploy and invoke**
752+
753+
```bash
754+
faas-cli up \
755+
--filter ecr-create-repo \
756+
--tag digest
757+
758+
# Create a new ECR repository
759+
curl -X POST http://127.0.0.1:8080/function/ecr-create-repo \
760+
-H "Content-Type: application/json" \
761+
-d '{"name":"tenant1/fn1"}'
762+
```
763+
764+
The response contains the ARN of the newly created repository:
765+
766+
```json
767+
{"arn": "arn:aws:ecr:eu-west-1:ACCOUNT_NUMBER:repository/tenant1/fn1"}
768+
```
769+
589770
## Example: Publish messages to Kafka
590771

591772
This example shows how to produce messages to a Kafka topic from a Python function using the `confluent-kafka` package with SASL/SSL authentication.

0 commit comments

Comments
 (0)