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-s3obs-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
main.tf¶
This files contains all resources to be created for this sample.
###########################################################
# 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
]
}
}
##########################################################
# Create Function
##########################################################
resource "opentelekomcloud_fgs_function_v2" "MyFunction" {
# depends_on = [opentelekomcloud_obs_bucket_object.code_object]
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 = "obs"
#code_url = format("https://%s/code/%s", opentelekomcloud_obs_bucket.codebucket.bucket_domain_name, basename(var.zip_file_local))
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
}
# lifecycle {
# # replace if code in bucket changed
# replace_triggered_by = [
# terraform_data.replacement
# ]
# }
}
##########################################################
# 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
}
}
##########################################################
# 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
}
}
##########################################################
# 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"))
})
}
##########################################################
# 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"
}
}
}]
}))
}
output "INPUT_BUCKET" {
value = opentelekomcloud_s3_bucket.inbucket.bucket
}
output "OUTPUT_BUCKET" {
value = opentelekomcloud_s3_bucket.outbucket.bucket
}
provider.tf¶
This file contains the terraform provider configuration.
Note
Check especially the backend “s3” configuration for bucket and key.
# ----------------------------------------------------------------------------
# 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.36.52"
}
}
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%