Skip to content

Commit 81cee2f

Browse files
authored
Merge pull request #2573 from ragibmahsan/ragibmahsan-feature-okta-eb-lambda
New serverless pattern - okta-eventbridge-lambda
2 parents 5cb6cd8 + 27dc604 commit 81cee2f

File tree

14 files changed

+538
-0
lines changed

14 files changed

+538
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Okta Amazon EventBridge Partner Integration to AWS Lambda
2+
3+
This pattern demonstrates how to use the Okta Amazon EventBridge integration and AWS Lambda to process events from Okta. This pattern is leveraging the Okta Amazon EventBridge integration to send Okta System Log events from the customer's Okta account to their AWS account, via an Amazon EventBridge Partner event bus. Once the Okta events are in the customer's account, an Amazon EventBridge rule routes unusual location user login events to a downstream Lambda function. In production cases, the Lambda function could transform the event, send it to a downstream application, archive it in a warehouse service, or send a notification email to a customer using SES. Amazon CloudWatch Log Groups are provisioned for debugging and auditing. This pattern deploys two EventBridge rules, one Lambda function, and two CloudWatch Log Groups.
4+
5+
Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/okta-eventbridge-lambda
6+
7+
Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.
8+
9+
## Requirements
10+
11+
* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
12+
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
13+
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
14+
* [AWS CDK CLI](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) (AWS CDK) installed
15+
* [Create an Okta Organization](https://www.okta.com/okta-advantage/?utm_source=google&utm_campaign=amer_mult_usa_all_wf-all_dg-ao_a-wf_search_google_text_kw_workforce-OktaBrand-exact_utm2&utm_medium=cpc&utm_id=aNK4z0000004DlbGAE&utm_term=okta&utm_page={url}&utm_content=679261513163&gad_source=1&gclid=CjwKCAjwnK60BhA9EiwAmpHZwy2ms0vms2fFPJZr0aECIlwkSs6Qs3U03pzFghn_wajg7chitFT1cRoCRKwQAvD_BwE) if you do not already have one and log in.
16+
* [Set up the Okta Amazon EventBridge integration](https://help.okta.com/en-us/content/topics/reports/log-streaming/add-aws-eb-log-stream.htm) if you have not already configured the integration.
17+
18+
19+
## Deployment Instructions
20+
21+
1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
22+
```
23+
git clone https://github.com/aws-samples/serverless-patterns
24+
```
25+
2. Change directory to the pattern directory:
26+
```
27+
cd eventbridge-partner-sources/1-okta/cdk-python
28+
```
29+
3. From the command line, use AWS CDK to deploy the AWS resources for the pattern as specified in the app.py file. A command-line argument is needed to deploy the CDK stack, "oktaEventBusName". This argument is the name of the **SaaS event bus** associated with your Okta [partner event source](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-saas.html).
30+
```
31+
cdk deploy --parameters oktaEventBusName=SAAS_EVENT_BUS_NAME_HERE
32+
```
33+
34+
4. Note the outputs from the CDK deployment process. These contain the resource names and/or ARNs which are used for testing. This stack will output the name of the Lambda function deployed for testing to the CLI. See the example below.
35+
36+
```
37+
Outputs:
38+
OktaIntegrationStack.OktaProcessUserLoginUnusualLocationLambdaOutput = OktaIntegrationStack-OktaProcessUserLoginUnusualLocationLambd....
39+
```
40+
41+
## How it works
42+
43+
This service interaction uses an existing Okta System integration in the customer's AWS account. If you do not have the Okta System integration set up in your AWS account, please set it up before deploying this pattern. View the [integration on Okta's site](https://help.okta.com/en-us/content/topics/reports/log-streaming/add-aws-eb-log-stream.htm).
44+
45+
This pattern demonstrates how to:
46+
1. Write EventBridge rules that match Okta's event pattern
47+
2. Send events from Okta's EventBridge integration to Amazon CloudWatch for logging and debugging
48+
3. Transform Okta events using AWS Lambda and allows you to connect to other services going forward
49+
50+
See the below architecture diagram from the data flow of this pattern.
51+
52+
![Architecture Diagram](./img/readme-arch-diagram.png)
53+
54+
## Testing
55+
56+
### Test the AWS Lambda Function
57+
58+
The event.json file included in this pattern is a sample EventBridge event from Okta. This event can be used to test the Lambda function and EventBridge rules deployed by this pattern.
59+
60+
To test the Lambda function via the CLI, copy and paste the following command, replacing the variables in <> with your own values:
61+
```
62+
aws lambda invoke --function-name <OktaIntegrationStack-OktaProcessUserLoginUnusual-....> --payload file://event.json --cli-binary-format raw-in-base64-out response.json
63+
```
64+
65+
You should receive a response that looks like:
66+
```
67+
{
68+
"StatusCode": 200,
69+
"ExecutedVersion": "$LATEST"
70+
}
71+
```
72+
73+
The command creates a response.json file in your directory. If you open this file, you see the output of the Lambda function.
74+
75+
### Test the EventBridge Rule
76+
77+
Only Okta can publish events to the event bus. To test the EventBridge rules deployed by this pattern, follow these instructions:
78+
79+
1. Navigate to the Amazon EventBridge console. Select "Rules".
80+
81+
2. From the Event bus list, choose the SaaS event bus associated with your Okta partner event source.
82+
83+
3. From the Rules list, select the "OktaIntegrationStack-OktaSpecficEventsRule54D..."
84+
85+
![EventBridge Console](./img/EBconsole-rules.png)
86+
87+
4. Choose "Edit" to enter the rule editor. Click through to "Step 2. Build Event Pattern."
88+
89+
![EventBridge Console](./img/BuildEvent.png)
90+
91+
5. Scroll down to "Sample event - optional." Select "Enter my own," and delete the pre-populated event. Copy the contents of event.json into the event editor.
92+
93+
![EventBridge Console](./img/SampleEvent.png)
94+
95+
6. Scroll down to "Event pattern." Choose "Test Pattern."
96+
97+
![EventBridge Console](./img/TestEvent.png)
98+
99+
A successful test results in a "Sample event matched the event pattern." message. This means that the rule will successfully route incoming events to the AWS Lambda function.
100+
101+
![EventBridge Console](./img/TestEventSuccessful.png)
102+
103+
104+
## Cleanup
105+
106+
1. Delete the stack
107+
```bash
108+
cdk destroy
109+
```
110+
111+
----
112+
Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
113+
114+
SPDX-License-Identifier: MIT-0
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python3
2+
import os
3+
4+
import aws_cdk as cdk
5+
from aws_cdk import (
6+
Stack,
7+
aws_s3 as s3,
8+
aws_lambda as _lambda,
9+
RemovalPolicy,
10+
aws_events as events,
11+
aws_events_targets as targets,
12+
aws_iam as iam,
13+
aws_logs as logs,
14+
Duration,
15+
CfnParameter,
16+
CfnOutput
17+
)
18+
19+
from constructs import Construct
20+
21+
class OktaIntegrationStack(Stack):
22+
23+
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
24+
super().__init__(scope, construct_id, **kwargs)
25+
26+
# Stack inputs
27+
OKTA_PARTNER_EVENT_BUS = CfnParameter(self, id="oktaEventBusName", type="String",
28+
description="The name of event bus the Okta Partner EventSource is associated with.")
29+
30+
# Turn partner event bus from string to IEventBus object
31+
partner_event_bus = events.EventBus.from_event_bus_name(scope=self, id='partner-event-bus', event_bus_name=OKTA_PARTNER_EVENT_BUS.value_as_string)
32+
33+
# CloudWatch Logs Group that stores all events sent by Okta for debugging or archive
34+
log_group = logs.LogGroup(
35+
self, "Okta-all-events",
36+
retention=logs.RetentionDays.ONE_DAY,
37+
removal_policy=RemovalPolicy.DESTROY
38+
)
39+
40+
# CloudWatch Logs Group that stores specific login location events from Okta for debugging or archive
41+
log_group_user_login_unusual_location_app = logs.LogGroup(
42+
self, "Okta-user-login-unusual-location-events",
43+
retention=logs.RetentionDays.ONE_DAY,
44+
removal_policy=RemovalPolicy.DESTROY
45+
)
46+
47+
lambda_role = iam.Role(scope=self, id='okta-cdk-lambda-role',
48+
assumed_by=iam.ServicePrincipal('lambda.amazonaws.com'),
49+
managed_policies=[
50+
iam.ManagedPolicy.from_aws_managed_policy_name(
51+
'service-role/AWSLambdaBasicExecutionRole')
52+
]
53+
)
54+
55+
# A Lambda function to consume and process specific events
56+
okta_process_user_login_unusual_location_events_lambda = _lambda.Function(
57+
self,
58+
id='OktaProcessUserLoginUnusualLocation',
59+
runtime=_lambda.Runtime.PYTHON_3_12,
60+
code=_lambda.Code.from_asset('src'),
61+
handler='OktaProcessUserLoginUnusualLocation.handler',
62+
role=lambda_role,
63+
timeout=Duration.seconds(15)
64+
)
65+
66+
# EventBridge Okta all events rule
67+
okta_all_events_rule = events.Rule(
68+
self,
69+
id="OktaAllEventsRule",
70+
event_bus=partner_event_bus
71+
)
72+
73+
# Add event pattern to rule
74+
okta_all_events_rule.add_event_pattern(
75+
source=events.Match.prefix('aws.partner/okta.com/evership/evershipsecuritylake'),
76+
)
77+
78+
# CloudWatch Log Group as target for EventBridge Rule
79+
okta_all_events_rule.add_target(targets.CloudWatchLogGroup(log_group))
80+
81+
# EventBridge Okta user unusual login location events
82+
okta_user_access_admin_app_rule = events.Rule(
83+
self,
84+
id="OktaUnusualLoginEventsRule",
85+
event_bus=partner_event_bus
86+
)
87+
88+
# Add rule to the event bus for specific events
89+
okta_user_access_admin_app_rule.add_event_pattern(
90+
source=events.Match.prefix('aws.partner/okta.com/evership/evershipsecuritylake'),
91+
detail_type=["SystemLog"],
92+
detail={
93+
"eventType": ["user.session.access_admin_app"]
94+
}
95+
)
96+
97+
# Lambda as target for EventBridge Rule
98+
okta_user_access_admin_app_rule.add_target(targets.LambdaFunction(okta_process_user_login_unusual_location_events_lambda))
99+
100+
# CloudWatch Log Group as target for EventBridge Rule
101+
okta_user_access_admin_app_rule.add_target(targets.CloudWatchLogGroup(log_group_user_login_unusual_location_app))
102+
103+
# Print the Lambda function name
104+
CfnOutput(self, "OktaProcessUserLoginUnusualLocationLambdaOutput", value=okta_process_user_login_unusual_location_events_lambda.function_name)
105+
106+
app = cdk.App()
107+
description = (
108+
"Okta EventBridge Integration (uksb-1tthgi812) (tag:eventbridge-partner-sources-okta-cdk-python)"
109+
)
110+
OktaIntegrationStack(app, "OktaIntegrationStack", description=description)
111+
app.synth()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"app": "python3 app.py",
3+
"watch": {
4+
"include": [
5+
"**"
6+
],
7+
"exclude": [
8+
"README.md",
9+
"cdk*.json",
10+
"requirements*.txt",
11+
"source.bat",
12+
"**/__init__.py",
13+
"python/__pycache__",
14+
"tests"
15+
]
16+
},
17+
"context": {
18+
"@aws-cdk/core:stackRelativeExports": true,
19+
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
20+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
21+
"@aws-cdk/core:target-partitions": [
22+
"aws"
23+
]
24+
}
25+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
{
2+
"version": "0",
3+
"id": "4ab6d852-09e9-1036-fc04-2e22004b3c3f",
4+
"detail-type": "SystemLog",
5+
"source": "aws.partner/okta.com/evership/evershipsecuritylake",
6+
"account": "999999999999",
7+
"time": "2023-05-30T14:17:58Z",
8+
"region": "us-east-1",
9+
"resources": [],
10+
"detail": {
11+
"actor": {
12+
"id": "00uttidj04jqI21bA1d6",
13+
"type": "User",
14+
"alternateId": "[email protected]",
15+
"displayName": "A User",
16+
"detailEntry": null
17+
},
18+
"client": {
19+
"userAgent": {
20+
"rawUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36",
21+
"os": "Mac OS X",
22+
"browser": "CHROME"
23+
},
24+
"zone": "null",
25+
"device": "Computer",
26+
"id": null,
27+
"ipAddress": "127.0.0.1",
28+
"geographicalContext": {
29+
"city": "Fictionville",
30+
"state": "Pennsylvania",
31+
"country": "United States",
32+
"postalCode": "19513",
33+
"geolocation": {
34+
"lat": 41.1286,
35+
"lon": -73.4835
36+
}
37+
}
38+
},
39+
"device": null,
40+
"authenticationContext": {
41+
"authenticationProvider": null,
42+
"credentialProvider": null,
43+
"credentialType": null,
44+
"issuer": null,
45+
"interface": null,
46+
"authenticationStep": 0,
47+
"externalSessionId": "102BoThue9qT2uRBdaO_Z9msg"
48+
},
49+
"displayMessage": "User accessing Okta admin app",
50+
"eventType": "user.session.access_admin_app",
51+
"outcome": {
52+
"result": "SUCCESS",
53+
"reason": null
54+
},
55+
"published": "2023-05-30T14:17:58.126Z",
56+
"securityContext": {
57+
"asNumber": 6167,
58+
"asOrg": "verizon",
59+
"isp": "verizon",
60+
"domain": "myvzw.com",
61+
"isProxy": false
62+
},
63+
"severity": "INFO",
64+
"debugContext": {
65+
"debugData": {
66+
"requestId": "ZHYFlX6QY0rHqq1oihP7CwAACSI",
67+
"dtHash": "e463841eed07369aeb7ace43a41fcef75ccefa573ced0420039c16b0e3d7cc99",
68+
"requestUri": "/admin/sso/callback",
69+
"url": "/admin/sso/callback?code=******&state=vdC6CnQXeZqyxBJKBVmtej9wMnF4nM1r"
70+
}
71+
},
72+
"legacyEventType": "app.admin.sso.login.success",
73+
"transaction": {
74+
"type": "WEB",
75+
"id": "ZHYFlX6QY0rHqq1oihP7CwAACSI",
76+
"detail": {}
77+
},
78+
"uuid": "c6ed294a-fef4-11ed-a5b1-bbb7c1de1a4b",
79+
"version": "0",
80+
"request": {
81+
"ipChain": [
82+
{
83+
"ip": "127.0.0.1",
84+
"geographicalContext": {
85+
"city": "Fictionville",
86+
"state": "Pennsylvania",
87+
"country": "United States",
88+
"postalCode": "19513",
89+
"geolocation": {
90+
"lat": 41.1286,
91+
"lon": -73.4835
92+
}
93+
},
94+
"version": "V4",
95+
"source": null
96+
}
97+
]
98+
},
99+
"target": [
100+
{
101+
"id": "00uttidj04jqI21bA1d6",
102+
"type": "AppUser",
103+
"alternateId": "[email protected]",
104+
"displayName": "A User",
105+
"detailEntry": null
106+
}
107+
]
108+
}
109+
}

0 commit comments

Comments
 (0)