S3 Event Thumbnail Sample¶
This is a sample that processes an image uploaded to OBS, resizes it to fit within a maximum dimension, and uploads the resized image back to another OBS using FunctionGraph with OBS trigger event.
Overview¶
Following diagram shows components used in this example:
Source¶
Source for this sample can be found in: /samples-doc/event-obss3-thumbnail.
Deployment¶
This sample can be deployed using Terraform, see Prepare the Terraform environment for setup details.
Terraform deployment scripts can be found in: /samples-doc/event-obss3-thumbnail/terraform
Makefile¶
Adapt file Makefile variables according to your needs.
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))/..
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
BACKEND_CONFIG_BUCKET := "doc-samples-tf-backend"
BACKEND_CONFIG_KEY := "terraform_state/csharp/doc-sample-event-obss3-thumbnail.tf"
BACKEND_CONFIG_REGION := "eu-de"
BACKEND_CONFIG_ENDPOINTS := "endpoints={s3=\"https://obs.eu-de.otc.t-systems.com\"}"
clean:
rm -f $(mkfile_path)/src/*.zip
rm -rf $(mkfile_path)/src/bin
rm -rf $(mkfile_path)/src/obj
build:
dotnet build
all: build
release:
dotnet build -c Release
initTerraform:
terraform -chdir=terraform \
init \
-backend-config=$(BACKEND_CONFIG_ENDPOINTS) \
-backend-config="bucket=$(BACKEND_CONFIG_BUCKET)" \
-backend-config="key=$(BACKEND_CONFIG_KEY)" \
-backend-config="region=$(BACKEND_CONFIG_REGION)"
plan: release
if [ ! -f "terraform/.terraform.lock.hcl" ]; then \
$(MAKE) initTerraform; \
fi
terraform -chdir=terraform \
plan \
-var-file="net6.tfvars"
deploy: release
if [ ! -f "terraform/.terraform.lock.hcl" ]; then \
$(MAKE) initTerraform; \
fi
terraform -chdir=terraform \
apply -auto-approve \
-var-file="net6.tfvars"
destroy:
terraform -chdir=terraform \
destroy -auto-approve \
-var-file="net6.tfvars"
.PHONY: build zip clean all release deploy destroy initTerraform plan
Terraform files¶
Agency and permissions
agency.tf¶########################################################### # Custom role to allow FunctionGraph to access LTS and OBS ########################################################### resource "opentelekomcloud_identity_role_v3" "role" { display_name = format("%s-%s-role", var.prefix, var.function_name) description = "Role for FunctionGraph to access OBS" display_layer = "project" statement { effect = "Allow" action = [ "functiongraph:*:*", "lts:*:*", ] } statement { effect = "Allow" action = [ "obs:*:*", ] resource = [ "OBS:*:*:object:*", format("OBS:*:*:bucket:%s", opentelekomcloud_s3_bucket.inbucket.bucket), format("OBS:*:*:bucket:%s", opentelekomcloud_s3_bucket.outbucket.bucket), ] } } ########################################################## # Agency for FunctionGraph # Attention: Crating agency will take some time. # Calls to function after creating agency will fail until # agency is set up. ########################################################## resource "opentelekomcloud_identity_agency_v3" "agency" { depends_on = [opentelekomcloud_identity_role_v3.role] delegated_domain_name = "op_svc_cff" name = format("%s-%s-agency", var.prefix, var.function_name) description = "Agency for FunctionGraph to access OBS" project_role { all_projects = true project = "eu-de" roles = [ opentelekomcloud_identity_role_v3.role.display_name ] } }
Input and output buckets
buckets.tf¶########################################################## # Input bucket for source images ########################################################## resource "opentelekomcloud_s3_bucket" "inbucket" { bucket = lower(format("%s-%s-%s", var.prefix, var.function_name, "images")) acl = "private" # Warning: force_destroy will delete bucket on # terraform destroy even if it contains objects force_destroy = true tags = { "app_group" = var.tag_app_group } } ########################################################## # Output bucket for thumbnail images # For output a different bucket is used to avoid potential # risk of recursive invocation of FunctionGraph ########################################################## resource "opentelekomcloud_s3_bucket" "outbucket" { bucket = lower(format("%s-%s-%s", var.prefix, var.function_name, "images-output")) acl = "private" # Warning: force_destroy will delete bucket on # terraform destroy even if it contains objects force_destroy = true tags = { "app_group" = var.tag_app_group } } output "INPUT_BUCKET" { value = opentelekomcloud_s3_bucket.inbucket.bucket } output "OUTPUT_BUCKET" { value = opentelekomcloud_s3_bucket.outbucket.bucket }
FunctionGraph function
function.tf¶########################################################## # Create Function ########################################################## resource "opentelekomcloud_fgs_function_v2" "MyFunction" { name = format("%s_%s", var.prefix, var.function_name) app = "default" agency = opentelekomcloud_identity_agency_v3.agency.name handler = var.function_handler_name initializer_handler = var.function_initializer_name initializer_timeout = 30 description = "Sample on how to create Thumbnails from images uploaded to OBS" memory_size = 512 timeout = 30 max_instance_num = 1 runtime = var.function_runtime code_type = "zip" func_code = filebase64(var.zip_file_local) code_filename = basename(var.zip_file_local) log_group_id = opentelekomcloud_lts_group_v2.MyLogGroup.id log_group_name = opentelekomcloud_lts_group_v2.MyLogGroup.group_name log_topic_id = opentelekomcloud_lts_stream_v2.MyLogStream.id log_topic_name = opentelekomcloud_lts_stream_v2.MyLogStream.stream_name # set some environment variables user_data = jsonencode({ "OUTPUT_BUCKET" : opentelekomcloud_s3_bucket.outbucket.bucket, "OBS_ENDPOINT" : "https://obs.otc.t-systems.com", # "RUNTIME_LOG_LEVEL" : "ERROR", # "RUNTIME_LOG_PATH" : "/tmp" }) tags = { "app_group" = var.tag_app_group } }
LogGroup and LogStream
loggroup.tf¶########################################################## # Create Log Group ########################################################## resource "opentelekomcloud_lts_group_v2" "MyLogGroup" { group_name = format("%s_%s_%s", var.prefix, var.function_name, "log_group") ttl_in_days = 1 tags = { "app_group" = var.tag_app_group } } ########################################################## # Create Log Stream ########################################################## resource "opentelekomcloud_lts_stream_v2" "MyLogStream" { group_id = opentelekomcloud_lts_group_v2.MyLogGroup.id stream_name = format("%s_%s_%s", var.prefix, var.function_name, "log_stream") tags = { "app_group" = var.tag_app_group } }
OBS trigger
trigger_obs.tf¶########################################################## # Create OBS Trigger listening for "ObjectCreate" in # input bucket ########################################################## resource "opentelekomcloud_fgs_trigger_v2" "obstrigger" { function_urn = opentelekomcloud_fgs_function_v2.MyFunction.urn type = "OBS" event_data = jsonencode({ "bucket" : opentelekomcloud_s3_bucket.inbucket.bucket "events" : [ "s3:ObjectCreated:*" ] "name" : lower(format("%s-%s-%s", var.prefix, var.function_name, "event")) }) }
Test event
testevent.tf¶########################################################## # Create Test Event ########################################################## resource "opentelekomcloud_fgs_event_v2" "test_event" { function_urn = opentelekomcloud_fgs_function_v2.MyFunction.urn name = "UploadTest" content = base64encode(jsonencode({ "Records" = [{ "eventVersion" = "2.0" "eventSource" = "obs" "eventTime" = "2025-10-24T08:30:00+08:00" "eventName" = "ObjectCreated:PutObject" "awsRegion" = "eu-de" "userIdentity" = { "principalId" = "EXAMPLE" } "requestParameters" = { "sourceIPAddress" = "EXAMPLE" } "s3" = { "configurationId" = "testConfigRule" "bucket" = { "name" = opentelekomcloud_s3_bucket.inbucket.id "ownerIdentity" = { "principalId" = "EXAMPLE" } "arn" = opentelekomcloud_s3_bucket.inbucket.arn } "object" = { "key" = "image.jpg" "size" = 1024 "eTag" = "0123456789abcdef0123456789abcdef" "sequencer" = "0A1B2C3D4E5F678901" } } }] })) }
Terraform Provider
This file contains the terraform provider configuration.
Note
Check especially the backend “s3” configuration for bucket and key.
provider.tf¶# ---------------------------------------------------------------------------- # Secret variables to be injected as envvar (capital letters for Windows systems) # - no defaults # - Declared as sensitive --> Not printed in console or log if used in resources # ---------------------------------------------------------------------------- # set by environment variable TF_VAR_OTC_SDK_AK variable "OTC_SDK_AK" { description = "Personal access key" type = string sensitive = true } # set by environment variable TF_VAR_OTC_SDK_SK variable "OTC_SDK_SK" { description = "Personal secret key" type = string sensitive = true } # set by environment variable TF_VAR_OTC_SDK_DOMAIN_NAME variable "OTC_SDK_DOMAIN_NAME" { description = "Domain Name, eg. OTC-EU-DE-000000000010000XXXXX" type = string } # set by environment variable TF_VAR_OTC_SDK_PROJECTID variable "OTC_SDK_PROJECTID" { description = "Project Id" type = string } # set by environment variable TF_VAR_OTC_SDK_PROJECTNAME variable "OTC_SDK_PROJECTNAME" { description = "Project Name, eg. eu-de_MYPROJECT" type = string } # set by environment variable TF_VAR_OTC_IAM_ENDPOINT variable "OTC_IAM_ENDPOINT" { description = "IAM Endpoint" type = string default = "https://iam.eu-de.otc.t-systems.com/v3" } terraform { required_providers { # specifies required provider, source and version # see https://registry.terraform.io/providers/opentelekomcloud/opentelekomcloud/latest opentelekomcloud = { source = "opentelekomcloud/opentelekomcloud" version = ">= 1.37.1" } } backend "s3" { # See: https://registry.terraform.io/providers/opentelekomcloud/opentelekomcloud/latest/docs/guides/backends # (Required) Specifies the endpoint for OpenTelekomCloud OBS. # The value is https://obs.{{region}}.otc.t-systems.com. # This can also be sourced from the AWS_S3_ENDPOINT environment variable endpoints = { s3 = "https://obs.eu-de.otc.t-systems.com" } # (Required) Specifies the bucket name where to store the state. # Make sure to create it before. bucket = "<your-bucket-name>" # (Required) Specifies the path to the state file inside the bucket. key = "<path/to/your/terraform.tfstate>" # (Required) Specifies the region where the bucket is located. # This can also be sourced from the AWS_DEFAULT_REGION and # AWS_REGION environment variables. region = "<your-region>" # (Required) Skip credentials validation via the STS API. # It's mandatory for OpenTelekomCloud. skip_credentials_validation = true # (Required) Skip validation of provided region name. # It's mandatory for OpenTelekomCloud. skip_region_validation = true skip_requesting_account_id = true # (Required) Skip usage of EC2 Metadata API. # It's mandatory for OpenTelekomCloud. skip_metadata_api_check = true # (Optional) Do not include checksum when uploading S3 Objects. # Useful for some S3-Compatible APIs. skip_s3_checksum = true # Although the terraform block does not accept variables or locals and # all backend configuration values must be hardcoded, you can provide # the credentials via the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY # environment variables to access OBS, respectively: # # export AWS_ACCESS_KEY_ID="your accesskey" # export AWS_SECRET_ACCESS_KEY="your secretkey" # # secret_key set env var: AWS_ACCESS_KEY_ID # access_key set env var: AWS_SECRET_ACCESS_KEY } } # ---------------------------------------------------------------------------- # Providers settings --> OTC # We use the AKSK auth scheme # See https://registry.terraform.io/providers/opentelekomcloud/opentelekomcloud/latest/docs # ---------------------------------------------------------------------------- # provider "opentelekomcloud" { auth_url = var.OTC_IAM_ENDPOINT access_key = var.OTC_SDK_AK secret_key = var.OTC_SDK_SK domain_name = var.OTC_SDK_DOMAIN_NAME tenant_name = var.OTC_SDK_PROJECTNAME }
To deploy use following command in directory where Makefile is located:
make deploy
Note
This terraform deployment creates an agency with permissions for FunctionGraph to access OBS.
The creation of an agency with permissions is a time consuming task and may take up to several minutes to complete.
Until the agency is fully, testing the function may fail with permission errors like:
`Error fetching object otc.jpg from bucket csharp-doc-sample-event-obss3-thumbnail-csharp-images: One or more errors occurred. (Access Denied)`
References¶
In this sample the AWS SDK for .Net is used to access OBS service:
<ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.4.3" />
<PackageReference Include="AWSSDK.Core" Version="3.7.4.3" />
</ItemGroup>
Documentation on the AWS SDK for .Net can be found here: Amazon S3 examples using SDK for .NET
Upload image file to source bucket¶
Linux/Ubuntu¶
Note
For s3cmd tool installation see: S3cmd on github.
To upload image to source bucket, following script an be used for Ubuntu users:
# https://github.com/opentelekomcloud/obs-s3/blob/master/s3cmd/README.md
# For bucket name see output of terraform output
OBS_INPUT_BUCKET="csharp-doc-sample-event-s3obs-thumbnail-csharp-images"
s3cmd --access_key=${OTC_SDK_AK} --secret_key=${OTC_SDK_SK} --no-ssl \
put ./test/resources/otc.jpg \
s3://$OBS_INPUT_BUCKET/otc.jpg
Microsoft Windows¶
Note
For Microsoft Windows, see OBS Browser+
Alternatives (unsupported)¶
Huawei obsutil¶
Note
Huawei obsutil is available for Windows, Linux and Mac from Huawei, see: obsutil Introduction
REM ########################################################################
REM Sample to upload picture to obs bucket using Huawei obsutil.
REM Huawei obsutil is available, see:
REM https://support.huaweicloud.com/intl/en-us/utiltg-obs/obs_11_0001.html
REM ########################################################################
REM for proxy use, set following environment variables
REM set HTTP_PROXY=proxy:port
REM set HTTPS_PROXY=proxy:port
REM For bucket name see output of terraform output
set OBS_INPUT_BUCKET="csharp-doc-sample-event-s3obs-thumbnail-csharp-images"
obsutil.exe cp .\test\resources\otc.jpg ^
obs://%OBS_INPUT_BUCKET%/otc.jpg ^
-e=https://obs.eu-de.otc.t-systems.com ^
-i=%ACCESS_KEY% ^
-k=%SECRET_ACCESS_KEY%