⚠️ DISCLAIMER: This is sample code intended for demonstration and non-production use only. Review and adapt security configurations before deploying to production environments.
This project demonstrates multiple integration patterns for connecting Moodle with AWS:
- Block Plugin: AI-powered translation block using Amazon Bedrock
- Local Plugin: Event observer that forwards Moodle events to AWS EventBridge for downstream processing
- LTI Integration: Learning Tools Interoperability (LTI) 1.3 integration for embedding AWS services in Moodle courses
- Web Services API: REST API integration for automated content indexing with vector embeddings
The solution includes:
- AWS CDK infrastructure (Python) for creating the necessary AWS resources
- React-based LTI frontend as a sample application
- Two Moodle plugins (AWS Events and AI Translator)
- Running Moodle instance (non production)
- Moodle user with sufficient privileges to install plugins
- A standard Moodle user e.g. student
-
mise - Development environment manager
macOS:
# Install mise brew install mise # Activate mise for current terminal session eval "$(mise activate bash)" # for bash eval "$(mise activate zsh)" # for zsh
Linux:
# Install mise curl https://mise.run | sh # Activate mise for current terminal session eval "$(mise activate bash)" # for bash eval "$(mise activate zsh)" # for zsh
Windows:
# Install mise winget install jdx.mise # Activate mise for current PowerShell session mise activate pwsh | Out-String | Invoke-Expression
-
Node.js 22 (managed by mise)
-
Python 3.13 (managed by mise))
-
AWS CLI (managed by mise on Mac and Linux. For Windows see the installation guide. This should be configured with appropriate credentials for the deployment account.
-
AWS CDK (
npm install -g aws-cdk) -
Finch or Docker running for container builds
For Docker users: Comment out the CDK_DOCKER line in mise.toml:
# CDK_DOCKER = "finch"-
AWS Account: You need a role (or less preferably IAM user) with sufficient permissions to deploy CDK stacks in the chosen region.
Deployment Permissions: The specific permissions required for deployment are documented in the
deployment-least-privilege-policy.jsonfile. This policy provides a reduced set of permissions focused specifically on the AWS services used by this stack, including:- CDK stack deployment and management
- AWS service provisioning (Lambda, API Gateway, EventBridge, etc.)
- Resource cleanup and updates
This policy is significantly more restrictive than using the default
AdministratorAccessand limits access to only the services needed by the CDK stack. You can use this policy document to create an IAM policy in your AWS account, or reference it when configuring deployment roles.Note: For production deployments, consider using more restrictive policies scoped to specific resource patterns and actions.
git clone https://github.com/aws-samples/sample-moodle-integrations-on-aws.git
cd sample-moodle-integrations-on-aws
mise trustConfigure AWS Region:
Update the AWS region in mise.toml to match your chosen deployment region:
# Edit mise.toml and update the AWS_REGION line:
AWS_REGION = "your-preferred-region"Copy the template and update with your values:
cp cdk.local.json.template cdk.local.jsonConfiguration parameters:
- domain_name: The domain name of your Moodle environment without the hostname (e.g., if your Moodle URL was
https://moodle.example.com,example.comshould be used). - host_name: Subdomain prefix for your Moodle instance (e.g., if your Moodle URL was
https://moodle.example.com,moodleshould be used). - moodle_role_name: (Optional) Name of an existing IAM role for AWS-hosted Moodle (e.g., EC2 instance role, ECS task role, or EKS service account role). If provided, the EventBridge policy will be automatically attached to this role. If empty, you must manually configure authentication using IAM Roles Anywhere (recommended) or IAM user with access keys.
Example for AWS-hosted Moodle:
{
"context": {
"domain_name": "example.com",
"host_name": "moodle",
"moodle_role_name": "MyEC2MoodleRole"
}
}Example for non-AWS hosted Moodle:
{
"context": {
"domain_name": "example.com",
"host_name": "moodle",
"moodle_role_name": ""
}
}IAM Permissions:
The CDK deployment creates a managed IAM policy called MoodleEventsIAMPolicy with the minimum permissions required for EventBridge access. The policy configuration depends on your moodle_role_name setting:
-
AWS-hosted Moodle (EC2/ECS/EKS): If you specified a
moodle_role_name, the deployment automatically attaches theMoodleEventsIAMPolicyto that existing role. Ensure the role name matches your EC2 instance role, ECS task role, or EKS service account role. -
Non-AWS hosted Moodle: If you left
moodle_role_nameempty, theMoodleEventsIAMPolicyis created but not attached to any role. You must configure authentication manually using one of the following approaches:Option 1: IAM Roles Anywhere (Recommended)
Use AWS IAM Roles Anywhere to authenticate your Moodle instance without long-lived access keys:
- Set up IAM Roles Anywhere with your certificate authority
- Create an IAM role for IAM Roles Anywhere
- Attach the
MoodleEventsIAMPolicy(created by this deployment) to your IAM Roles Anywhere role - Configure your Moodle server to use the IAM Roles Anywhere credentials
See AWS IAM Roles Anywhere documentation for detailed setup instructions.
Option 2: IAM User with Access Keys (Use Only When Necessary)
If IAM Roles Anywhere is not feasible, create an IAM user with IP-restricted permissions:
-
Create an IAM user (e.g.,
MoodleEventsUser) using the AWS Console or CLI -
Create a custom policy with IP address restrictions and attach it to your IAM user:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "events:PutEvents", "Resource": "arn:aws:events:REGION:ACCOUNT-ID:event-bus/moodle-events", "Condition": { "IpAddress": { "aws:SourceIp": [ "YOUR-MOODLE-SERVER-IP/32", "YOUR-BACKUP-SERVER-IP/32" ] } } } ] }Note: Use
/32for specific IP addresses. If your Moodle environment frequently changes IP addresses (e.g. container based environment), you can use broader CIDR ranges like192.168.1.0/24or10.0.0.0/16to cover the relevant subnets, but be aware this reduces security by allowing access from a wider range of IP addresses. -
Create access keys for the IAM user and store them securely
⚠️ Security Requirements for Access Keys:- Store access keys securely
- Rotate access keys regularly
- Monitor access key usage through CloudTrail
- Never commit access keys to version control
- Update IP addresses: If your Moodle server IP addresses change, update the policy condition accordingly
The IP address restriction in the policy above ensures that even if access keys are compromised, they can only be used from your authorized Moodle server locations.
By default, CDK uses the AdministratorAccess policy for CloudFormation execution. For enhanced security, you can optionally configure CDK to use least privilege permissions instead.
Leave POLICY_ARN empty in mise.toml:
POLICY_ARN = ""This uses the default AdministratorAccess policy for CloudFormation execution.
If you have an existing IAM policy with the minimum required permissions for CDK deployment, you can configure the deployment to use it:
-
Create the required IAM policy: Use the policy document provided in
deployment-least-privilege-policy.jsonto create an IAM policy with the minimum permissions required for this CDK deployment. -
Update mise.toml: Edit the
mise.tomlfile and update thePOLICY_ARNline with your policy ARN:POLICY_ARN = "arn:aws:iam::YOUR-ACCOUNT-ID:policy/YOUR-POLICY-NAME"
Security Note: When POLICY_ARN is set, both your deployment role and the CloudFormation execution role will use the same least privilege policy. When empty, CDK uses AdministratorAccess for CloudFormation execution.
mise run init
mise run cdk:bootstrap
mise run cdk:deploy
mise run packageImportant: Save the outputs from the cdk:deploy step, as these URLs and configuration values will be needed later when configuring the Moodle plugins.
Managed through mise.toml:
mise run init- Install all dependenciesmise run clean- Remove build artifacts
mise run cdk:bootstrap- Bootstrap CDK toolkit (usesPOLICY_ARNif set)mise run cdk:synth- Synthesize and check for security issuesmise run cdk:deploy- Deploy stack to AWSmise run cdk:sync- Watch and hot-reload changesmise run cdk:destroy- Destroy stack and cleanup
mise run site:build- Build LTI frontendmise run package- Package all Moodle pluginsmise run events:package- Package AWS Events plugin onlymise run aitranslator:package- Package AI Translator plugin only
If the sample is being adapted for production deployments, it's strongly recommended to use a custom domain name for the LTI CloudFront distribution instead of the default CloudFront domain. This enables important security configurations:
Security Benefits:
- SSL/TLS Policy Control: Custom domains allow you to configure minimum SSL/TLS versions (e.g., TLS 1.2 or higher)
- Certificate Management: Use your own SSL certificates with proper certificate chains
- Brand Consistency: Use your organization's domain for better user trust
⚠️ Production Security: Default CloudFront domains use shared SSL certificates and may not meet enterprise security requirements. Custom domains with proper SSL/TLS policies are essential for production deployments.
The deployment creates two plugin packages:
moodle/plugin/local/awsevents.zip- AWS Events pluginmoodle/plugin/blocks/aitranslator.zip- AI Translator block
Before configuring plugins, enable Moodle's web services. This creates REST API endpoints that allow AWS services to authenticate and access Moodle resources. Specifically, this is required for:
- AI Translator block: User authentication via Moodle web service tokens
- AWS Events plugin: File indexing capabilities that allow AWS Lambda functions to retrieve course content and files from Moodle
- Enable web services: Site Administration > Server > Web services > Overview
- Enable REST protocol: Site Administration > Server > Web services > Manage protocols
⚠️ Security Note: If web services are not currently enabled in your Moodle environment, make sure that you understand the risks of enabling these options and that you have appropriate controls and firewall rules in place to avoid unintended access to your Moodle environment. Web services create API endpoints that, if not properly secured, could expose sensitive data or functionality.
The block plugin follows the following architecture to integerate with the Moodle LMS.
The Moodle plugin calls an API Gateway endpoint backed by a Lambda function. It makes a POST request to interact with the Bedrock service.
-
Custom LMS Plugin Component: Native extension to the LMS that provides the UI and handles user interaction.
-
API Gateway: Secure endpoint that receives requests from the plugin.
-
AWS Lambda: Processes requests, formats prompts for the model, and handles responses.
-
Amazon Bedrock: Provides access to foundation models like Claude or Amazon Nova.
Install the AI Translator block by navigating to Site Administration > Plugins > Install plugins and uploading the zip file.
- If not automatically redirected to the configuration page, navigate to: Site Administration > Plugins > Blocks > AI Translator
- Configure:
- API Gateway URL: The CloudFormation stack output starting with
MoodleAiTranslatorTranslateEndpoint(e.g.,https://abc123.execute-api.us-west-2.amazonaws.com/v1/translate)
- API Gateway URL: The CloudFormation stack output starting with
Create a custom role and external service for users who should have access to the AI Translator:
- Create custom role: Site Administration > Users > Permissions > Define roles
- Click
Add a new role - Use role or archetype:
No role - Short name:
aitranslatorrole - Custom full name:
AI Translator Role - Context types where this role may be assigned:
System - Filter capabilities and allow:
webservice/rest:usemoodle/webservice:createtoken
- Click
Create this role
- Click
- Assign role to users: Site Administration > Users > Permissions > Assign system roles
- Select
AI Translator Role - Add users who should have access to the translator e.g. a student user (not the Moodle administrator)
- Select
- Create external service: Site Administration > Server > Web services > External services
- Click
Add - Name:
AI Translator Service - Short name:
ai_translator(⚠️ IMPORTANT: Must be exactlyai_translator- this is referenced from the plugin) - Enabled: ✓
- Authorized users only: ✓
- Click
Add service - Click
Add functions - Add function:
core_webservice_get_site_info - Click
Add functions
- Click
- Authorize users: Site Administration > Server > Web services > External services
- Click
Authorised usersfor AI Translator Service - Add users who should have access e.g. a student user (not the Moodle administrator)
- Click
Note: The
core_webservice_get_site_infofunction is required for API Gateway authorization. This role grants only the minimum permissions required for the AI Translator to function, following the principle of least privilege.
- Navigate to a course where you want to add the AI Translator block
- Turn editing on
- Click
Add a blockin the sidebar - Select
AI Translator - The block will appear in the sidebar
- Ensure you are logged in to Moodle with a user who has been authorized e.g. a student user (not the Moodle administrator)
- In the AI Translator block, enter text in the input field
- Click
Ask AI - The translated text will appear below
Verification:
The block uses the user's Moodle web service token to authenticate with API Gateway, which validates the token against Moodle's web service API before invoking the translation Lambda function backed by Amazon Bedrock. Check CloudWatch Logs for the translate Lambda function to verify API calls.
Note: Unlike the AWS Events plugin (below) which uses service credentials, the AI Translator block operates in the user's context. Each translation request is authenticated using a Moodle web service token generated for the logged-in user, ensuring user-level access control.
The events plugin follows the following architecture to integrate with the Moodle LMS.
When an event occurs in the LMS, the following process takes place:
-
LMS captures the event in its Events subsystem
-
Custom LMS Plugin observes each created event
-
The plugin sends an authenticated request to EventBridge or streaming service with the event payload
-
AWS API validates the request using IAM
-
Upon successful authentication, the request is added to an EventBridge event bus or stream
-
A rule receives incoming events and routes them to appropriate AWS Services based on matching event patterns or records are processed off the stream and processed by the consumer.
This architecture ensures secure communication between the LMS and AWS Services allowing LMS platform capabilities to be extended with AWS Services.
Install the AWS Events plugin by navigating to Site Administration > Plugins > Install plugins and uploading the zip file.
Note: The AWS Events plugin depends on the local_aws plugin for AWS SDK support. You will be given the option to install this during the installation if not already installed.
- If not automatically redirected to the configuration page, navigate to: Site Administration > Plugins > Local plugins > AWS Events
- Configure based on your authentication method:
For AWS-hosted Moodle or IAM Roles Anywhere:
- AWS Authentication Method: EC2 Instance Role
- AWS Region: Your deployment region (e.g., us-west-2)
- Event Bus Name:
moodle-events
Note: This configuration works for both AWS-hosted Moodle (EC2/ECS/EKS with
moodle_role_namespecified) and non-AWS hosted Moodle using IAM Roles Anywhere. IAM Roles Anywhere provides temporary credentials through the AWS credential chain, so the plugin treats it the same as an EC2 instance role.
For non-AWS hosted Moodle with Access Keys (use only when necessary):
- AWS Authentication Method: Access Keys
- AWS Region: Your deployment region (e.g., us-west-2)
- AWS Access Key: Your IAM user access key
- AWS Secret Key: Your IAM user secret key
- Event Bus Name:
moodle-events
Security Note: If using access keys, ensure they are stored securely, rotated regularly , and monitored through CloudTrail. Consider migrating to IAM Roles Anywhere for improved security.
Required for file indexing:
- Create API user: Site Administration > Server > Web services > Create a specific user
- Username:
index_files_api_user - Password: Random password (doesn't need to be used again)
- First name:
API - Last name:
IndexFiles - Email: unique email address
- Username:
- Create custom role: Site Administration > Users > Permissions > Define roles
- Click
Add a new role - Use role or archetype:
No role - Short name:
webservicefileaccessrole - Custom full name:
Web Service File Access Role - Context types where this role may be assigned:
System - Filter capabilities and allow:
webservice/rest:usemoodle/course:viewmoodle/course:viewhiddencourses
- Click
Create this role
- Click
- Assign role to user: Site Administration > Users > Permissions > Assign system roles
- Select
Web Service File Access Role - Add
API IndexFilesuser
- Select
- Create external service: Site Administration > Server > Web services > External services
- Click
Add - Name:
Index Files Service - Short name:
index_files - Enabled: ✓
- Authorized users only: ✓
- Can download files: ✓
- Click
Add Service - Add function:
- Name:
core_course_get_contents - Click
Add functions
- Name:
- Authorize user: Site Administration > Server > Web services > External services
- Select
Authorised usersagainst the IndexFiles service - Add the user
API IndexFilesas an Authorised user
- Select
- Click
- Create token: Site Administration > Server > Web services > Manage tokens
- Click
Create token - User:
API IndexFiles - Service:
IndexFiles - Valid until: unticked
- Click
- In AWS Secrets Manager update the Secret value of the secret with a name starting with
MoodleEventHandlersMoodleTowith the Token just generated
Verification:
After configuration, uploading a file to a Moodle course triggers a Lambda function that indexes it in the Bedrock Knowledge Base. Verify by checking the moodle-plugins-MoodleDataSource data source under the moodle-plugins-MoodleFiles Knowledge Base in the AWS Console.
The LTI tool follows the following architecture and flow to integrate with Moodle.
When a user interacts with the LTI plugin in the LMS:
-
The LMS and the LTI tool perform a handshake process that enables the exchange of information.
-
Upon success, the LTI tool is displayed inside an iFrame on the LMS.
-
The tool makes authenticated calls to an AWS backend.
-
The tool returns the requested information.
-
Alternatively, the tool can also pass information back to the LMS.
- Configure the LTI tool: Site Administration > Plugins > Activity modules > Manage tools > Configure a tool manually
- Enter details. Make sure to select LTI version as LTI 1.3. The Tool URL, Public keyset, and Initiate login URL can be found in the CloudFormation stack outputs
| Tool Parameter | Description | Cloudformation output |
|---|---|---|
| Tool name | The name of the tool | N/A |
| LTI Version | The version of LTI being used for signing messages and service requests should be set to LTI 1.3 | N/A |
| Tool URL | The URL of your tool's launch endpoint | LTIToolURL |
| Public keyset | The URL of an endpoint which will return a public key | LTIPublicKeyset |
| Initiate login URL | The URL of your tool's login endpoint | LTIInitiateLoginURL |
| Redirect URI (s) | List of URI (s) that Moodle can redirect to. You would usually add here at least your launch URI as Moodle redirects there as part of the handshake process | N/A |
- Once the tool is created, you can click on the magnifiying glass icon to view the parameters generated by Moodle. Copy the ClientID.
- On the AWS console, visit the Parameter store in the region where Moodle was deployed.
- Modify the /lti/client-id parameter with the one copied above.
- Back in Moodle, go to the course where you want to add the LTI tool
- Enable the tool in the activity chooser: More > LTI External Tools > Show in activity chooser
- Your tool should be available as an activity to add to a course.
Note: This LTI tool is a sample implementation intended for demonstration purposes only. It demonstrates the LTI 1.3 authentication flow and integration patterns but does not provide functionality. Use this as a reference for building functional LTI tools.
.
├── cdk/ # CDK infrastructure code
│ ├── constructs/ # Reusable CDK constructs
│ └── cdk_stack.py # Main stack definition
├── lambda/ # Lambda function code
│ ├── delay/ # Event delay handler
│ ├── index_moodle_file/ # File indexing
│ ├── lti/ # LTI integration
│ ├── moodle_authorizer/ # API Gateway authorizer
│ └── translate/ # Translation service
├── lti_frontend/ # React LTI frontend
├── moodle/plugin/ # Moodle plugins
│ ├── blocks/ # AI Translator block
│ └── local/ # AWS Events local plugin
├── layers/ # Lambda layers
├── app.py # CDK app entry point
├── cdk.json # CDK configuration
└── mise.toml # Task runner configuration
To remove all deployed AWS resources:
mise run cdk:destroyThis command will:
- Delete the CloudFormation stack and all associated resources
- Remove Lambda functions, API Gateway, EventBridge bus and OpenSearch domain
- Clean up CloudWatch log groups
Note: Some resources may incur costs until fully deleted. Verify deletion in the AWS Console.


