Reputation: 141
I've been trying to invoke a GCP function (--runtime nodejs8 --trigger-http
) from GCP scheduler, both located within the same project. I can only make it work, if I grant unauthenticated access by adding the allUsers
member to the functions permissions, with the Cloud Functions-Invoker
role applied to it. However, when I only use the service account of the scheduler as the Cloud Functions-Invoker
, I get a PERMISSION DENIED Error.
I created a hello world example, to show in detail, how my setup looks like.
gcloud iam service-accounts create scheduler --display-name="Task Schedule Runner"
svc_policy.json:
{
"bindings": [
{
"members": [
"serviceAccount:[email protected]"
],
"role": "roles/cloudscheduler.serviceAgent"
}
]
}
gcloud iam service-accounts set-iam-policy [email protected] svc_policy.json -q
gcloud functions deploy helloworld --runtime nodejs8 --trigger-http --entry-point=helloWorld
gcloud functions add-iam-policy-binding helloworld --member serviceAccount:[email protected] --role roles/cloudfunctions.invoker
gcloud beta scheduler jobs create http test-job --schedule "5 * * * *" --http-method=GET --uri=https://us-central1-mwsdata-1544225920485.cloudfunctions.net/helloworld --oidc-service-account-email=scheduler@mwsdata-1544225920485.iam.gserviceaccount.com --oidc-token-audience=https://us-central1-mwsdata-1544225920485.cloudfunctions.net/helloworld
Log: PERMISSION DENIED
{
httpRequest: {
}
insertId: "1ny5xuxf69w0ck"
jsonPayload: {
@type: "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished"
jobName: "projects/mwsdata-1544225920485/locations/europe-west1/jobs/test-job"
status: "PERMISSION_DENIED"
targetType: "HTTP"
url: "https://us-central1-mwsdata-1544225920485.cloudfunctions.net/helloworld"
}
logName: "projects/mwsdata-1544225920485/logs/cloudscheduler.googleapis.com%2Fexecutions"
receiveTimestamp: "2020-02-04T22:05:05.248707989Z"
resource: {
labels: {
job_id: "test-job"
location: "europe-west1"
project_id: "mwsdata-1544225920485"
}
type: "cloud_scheduler_job"
}
severity: "ERROR"
timestamp: "2020-02-04T22:05:05.248707989Z"
}
Here are the corresponding settings.
Scheduler Service Account
gcloud iam service-accounts get-iam-policy [email protected]
bindings:
- members:
- serviceAccount:[email protected]
role: roles/cloudscheduler.serviceAgent
etag: BwWdxuiGNv4=
version: 1
IAM Policy of the function:
gcloud functions get-iam-policy helloworld
bindings:
- members:
- serviceAccount:[email protected]
role: roles/cloudfunctions.invoker
etag: BwWdxyDGOAY=
version: 1
Function Description
gcloud functions describe helloworld
availableMemoryMb: 256
entryPoint: helloWorld
httpsTrigger:
url: https://us-central1-mwsdata-1544225920485.cloudfunctions.net/helloworld
ingressSettings: ALLOW_ALL
labels:
deployment-tool: cli-gcloud
name: projects/mwsdata-1544225920485/locations/us-central1/functions/helloworld
runtime: nodejs8
serviceAccountEmail: [email protected]
sourceUploadUrl: https://storage.googleapis.com/gcf-upload-us-central1-671641e6-3f1b-41a1-9ac1-558224a1638a/b4a0e407-69b9-4f3d-a00d-7543ac33e013.zip?GoogleAccessId=service-617967399269@gcf-admin-robot.iam.gserviceaccount.com&Expires=1580854835&Signature=S605ODVtOpnU4LIoRT2MnU4OQN3PqhpR0u2CjgcpRcZZUXstQ5kC%2F1rT6Lv2SusvUpBrCcU34Og2hK1QZ3dOPluzhq9cXEvg5MX1MMDyC5Y%2F7KGTibnV4ztFwrVMlZNTj5N%2FzTQn8a65T%2FwPBNUJWK0KrIUue3GemOQZ4l4fCf9v4a9h6MMjetLPCTLQ1BkyFUHrVnO312YDjSC3Ck7Le8OiXb7a%2BwXjTDtbawR20NZWfgCCVvL6iM9mDZSaVAYDzZ6l07eXHXPZfrEGgkn7vXN2ovMF%2BNGvwHvTx7pmur1yQaLM4vRRprjsnErU%2F3p4JO3tlbbFEf%2B69Wd9dyIKVA%3D%3D
status: ACTIVE
timeout: 60s
updateTime: '2020-02-04T21:51:15Z'
versionId: '1'
Scheduler Job Description
gcloud scheduler jobs describe test-job
attemptDeadline: 180s
httpTarget:
headers:
User-Agent: Google-Cloud-Scheduler
httpMethod: GET
oidcToken:
audience: https://us-central1-mwsdata-1544225920485.cloudfunctions.net/helloworld
serviceAccountEmail: [email protected]
uri: https://us-central1-mwsdata-1544225920485.cloudfunctions.net/helloworld
lastAttemptTime: '2020-02-05T09:05:00.054111Z'
name: projects/mwsdata-1544225920485/locations/europe-west1/jobs/test-job
retryConfig:
maxBackoffDuration: 3600s
maxDoublings: 16
maxRetryDuration: 0s
minBackoffDuration: 5s
schedule: 5 * * * *
scheduleTime: '2020-02-05T10:05:00.085854Z'
state: ENABLED
status:
code: 7
timeZone: Etc/UTC
userUpdateTime: '2020-02-04T22:02:31Z'
Upvotes: 14
Views: 12521
Reputation: 3181
Question is probably about first generation of Cloud Functions but maybe my struggle will help someone who is trying with second generation CF.
After quite a struggle with this I managed to setup proper permissions to second generation cloud function.
Few things I did not realize from the start:
my_cf_function => my-cf-function
)So what I did to make it work:
#!/bin/bash
set -e
SA_NAME="my-cf-sa"
PROJECT="my-project-id"
REGION="europe-west1"
SA_EMAIL="${SA_NAME}@${PROJECT}.iam.gserviceaccount.com"
IAM_MEMBER="serviceAccount:${SA_EMAIL}"
CLOUD_FUNCTION_NAME="my-http-function"
SCHEDULER_JOB_NAME="my-cf-job"
# Create SA to use with function
gcloud iam service-accounts create $SA_NAME \
--description="My CF SA" \
--display-name="My CF SA" \
--project=$PROJECT
# Deploy function with created SA
gcloud functions deploy $CLOUD_FUNCTION_NAME \
--gen2 \
--runtime=python311 \
--source=. \
--entry-point=my_http_function \
--trigger-http \
--service-account=$SA_EMAIL \
--run-service-account=$SA_EMAIL \
--no-allow-unauthenticated \
--region=$REGION \
--project=$PROJECT
# COMMAND BELOW DOES NOT WORK even though it is in documentation here https://cloud.google.com/scheduler/docs/http-target-auth
#
# Generate this error:
#
# ERROR: (gcloud.functions.add-iam-policy-binding) ResponseError: status=[400], code=[Ok], message=[Invalid argument: 'An invalid argument was specified. Please check the fields and try again.']
#
# Bug report for this: https://issuetracker.google.com/issues/284853816
#
# gcloud functions add-iam-policy-binding $CLOUD_FUNCTION_NAME --member=$IAM_MEMBER --role=roles/run.invoker --gen2 --region=$REGION --project=$PROJECT
# Add roles/cloudfunctions.invoker to allow SA to invoke function run
#
# After running this command it will ask you if you want to also add iam for Cloud Run service connected to CF.
#
#
# WARNING: The role [roles/cloudfunctions.invoker] was successfully bound to member [serviceAccount:[email protected]]
# but this does not grant the member permission to invoke 2nd gen function [my-http-function]. Instead, the role [roles/run.invoker] must be
# granted on the underlying Cloud Run service. This can be done by running the `gcloud functions add-invoker-policy-binding` command.
#
# Would you like to run this command and additionally grant [serviceAccount:[email protected]] permission to invoke function [my-http-function] (Y/n)?
#
# If you choose YES you don't need to manually run iam binding command for Cloud Run service.
# I choose NO and did this in two steps so I could replicate all this in terraform later on
gcloud functions add-iam-policy-binding $CLOUD_FUNCTION_NAME \
--member=$IAM_MEMBER \
--role=roles/cloudfunctions.invoker \
--gen2 \
--region=$REGION \
--project=$PROJECT
# Add roles/run.invoker to allow SA invoke Cloud Run service that is connected to function
#
# Only needed if you choose NO in previous command
gcloud run services add-iam-policy-binding $CLOUD_FUNCTION_NAME \
--member=$IAM_MEMBER \
--role=roles/run.invoker \
--region=$REGION \
--project=$PROJECT
# Create Scheduler job with OIDC auth set to use SA
gcloud scheduler jobs create http $SCHEDULER_JOB_NAME \
--schedule="0 */6 * * *" \
--uri="$(gcloud functions describe $CLOUD_FUNCTION_NAME --gen2 --project=$PROJECT --region=$REGION --format="value(serviceConfig.uri)")" \
--http-method=GET \
--oidc-service-account-email=$SA_EMAIL \
--location=$REGION \
--project=$PROJECT
You need to wait few mintes before actually running a job because IAM permissions may take some time to propagate everywhere.
After this I run job manually:
gcloud scheduler jobs run $SCHEDULER_JOB_NAME --location=$REGION --project=$PROJECT
I've checked logs and
gcloud logging read "resource.type=\"cloud_scheduler_job\" AND resource.labels.job_id=\"$SCHEDULER_JOB_NAME\" AND resource.labels.location=\"$REGION\"" --project=$PROJECT --limit 1
And I got 200 from CF
---
httpRequest:
status: 200
insertId: gde4phfur06nd
jsonPayload:
'@type': type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished
jobName: projects/my-project-id/locations/europe-west1/jobs/my-cf-job
targetType: HTTP
url: <FUNCTION_URL>
logName: projects/my-project-id/logs/cloudscheduler.googleapis.com%2Fexecutions
receiveTimestamp: '2023-06-03T16:40:28.454858025Z'
resource:
labels:
job_id: my-cf-job
location: europe-west1
project_id: my-project-id
type: cloud_scheduler_job
severity: INFO
timestamp: '2023-06-03T16:40:28.454858025Z'
Upvotes: 1
Reputation: 316
I had a similar issue.
In our case, we've enabled Cloud Scheduler quite a long time ago.
According to the docs, if you enabled Cloud Scheduler API
before March 19, 2019, you need to manually add the Cloud Scheduler Service Agent
role to your Cloud Scheduler
service account.
So we had to create a new service account that looks like this service-[project-number]@gcp-sa-cloudscheduler.iam.gserviceaccount.com
Hope this will help anybody else.
Upvotes: 5
Reputation: 704
this tutorial helped me to invoke a programmer function, but there is a problem when creating the program after creating the service account, finally eliminating the programmer and doing it again.
Google Cloud Scheduler - Calling Cloud Function
Upvotes: 0
Reputation: 315
@Marko I went through the same issue, it seems to re-enable (disable/enable) the scheduler API did the fix. This is why creating a new project makes sense because you probably got a scheduler service account by doing so. So if your project doesn't have a scheduler service account created from google, doing this trick will give you one. And although you don't need to assign this specific service account to any of your tasks, it must be available. You can see my work here: How to invoke Cloud Function from Cloud Scheduler with Authentication
Upvotes: 8
Reputation: 3764
Here are the steps I followed to make Cloud Scheduler trigger an HTTP triggered Cloud Function that doesn't allow unauthenticated invocations:
gcloud scheduler jobs create http [JOB-NAME] --schedule="* * * * *" --uri=[CLOUD-FUNCTIONS-URL] --oidc-service-account-email=[SA-NAME]@[PROJECT-ID].iam.gserviceaccount.com
In your specific case you are leaving the default App Engine service account for your Cloud Functions. Change it to the service account you created as specified on the previous steps.
Upvotes: 15