terraform - github_branch_protection: use username instead of node_id - github

I'm using GitHub Terraform provider with the resource github_branch_protection (documentation: https://registry.terraform.io/providers/integrations/github/latest/docs/resources/branch_protection )
I'm using the argument required_pull_request_reviews.pull_request_bypassers (https://registry.terraform.io/providers/integrations/github/latest/docs/resources/branch_protection#pull_request_bypassers)
The documentation says:
pull_request_bypassers: (Optional) The list of actor Names/IDs that are allowed to bypass pull request requirements. Actor names must either begin with a "/" for users or the organization name followed by a "/" for teams.
So according to it I should be able to use username. It's confirmed by the example also:
required_pull_request_reviews {
dismiss_stale_reviews = true
restrict_dismissals = true
dismissal_restrictions = [
data.github_user.example.node_id,
github_team.example.node_id,
"/exampleuser",
"exampleorganization/exampleteam",
]
}
(ok this example is using dismissal_restrictions but the documentation is the same about the use of actor Names/IDs)
The problem is that when I use /username I have the following error:
│ Error: Could not resolve to a node with the global id of '/username'
My terraform plan contains:
required_pull_request_reviews {
~ pull_request_bypassers = [
+ "/username",
]
}
Do you know what am I making wrong and how to use username instead of node_id?
Thanks

Related

Creating Issue Labels with Terraform using the Github Provider

I'm trying to automate my repository setup with terraform. First thing is creating issue labels for a bunch of repos using the Terraform Guthub provider.
It works when I explicitly state the repo and the labels:
terraform {
required_providers {
github = {
source = "integrations/github"
version = "~> 5.0"
}
}
}
# Use env-var
provider "github" {}
data "github_repository" "trashbox" {
full_name = "sebastian-sommerfeld-io/trashbox"
}
resource "github_issue_label" "bug" {
repository = data.github_repository.trashbox.id
name = "bug"
description = "Something is not working"
color = "B60205"
}
resource "github_issue_label" "security" {
repository = data.github_repository.trashbox.id
name = "security"
description = "CVEs, code scan violations, etc."
color = "cd3ad7"
}
But this would mean that I would have to duplicate everything for another repo. Or at least that I need to update my terraform config manually when I add another repo. I'd prever to have all relevant repos auto-detected.
Auto-detecting works with this snippet ... this returns all repos I want to configure.
data "github_repositories" "repos" {
query = "user:sebastian-sommerfeld-io archived:false"
include_repo_id = true
}
But now I cannot create the labels. When I run terraform apply I always get this error:
Error: POST https://api.github.com/repos/sebastian-sommerfeld-io/sebastian-sommerfeld-io/website-sommerfeld-io/labels: 404 Not Found []
with github_issue_label.bug["sebastian-sommerfeld-io/website-sommerfeld-io"],
on issues.tf line 1, in resource "github_issue_label" "bug":
1: resource "github_issue_label" "bug" {
The odd thing is, that terraform plan does not hint at any error:
# github_issue_label.bug["sebastian-sommerfeld-io/website-sommerfeld-io"] will be created
+ resource "github_issue_label" "bug" {
+ color = "B60205"
+ description = "Something is not working"
+ etag = (known after apply)
+ id = (known after apply)
+ name = "bug"
+ repository = "sebastian-sommerfeld-io/website-sommerfeld-io"
+ url = (known after apply)
}
My complete Terraform config which generates the outputs from terraform plan and terraform apply is this:
terraform {
required_providers {
github = {
source = "integrations/github"
version = "~> 5.0"
}
}
}
# Use env-var
provider "github" {}
data "github_repositories" "repos" {
query = "user:sebastian-sommerfeld-io archived:false"
include_repo_id = true
}
resource "github_issue_label" "bug" {
for_each = toset(data.github_repositories.repos.full_names)
repository = each.value
name = "bug"
description = "Something is not working"
color = "B60205"
}
The repositories are queried correctly. I confirmed this via:
output "affected_repos" {
value = data.github_repositories.repos.full_names
description = "Github Repos"
}
This lists all repos correctly:
affected_repos = tolist([
"sebastian-sommerfeld-io/website-sommerfeld-io",
"sebastian-sommerfeld-io/jarvis",
"sebastian-sommerfeld-io/github-action-generate-docs",
"sebastian-sommerfeld-io/configs",
"sebastian-sommerfeld-io/website-tafelboy-de",
"sebastian-sommerfeld-io/website-numero-uno-de",
"sebastian-sommerfeld-io/website-masterblender-de",
"sebastian-sommerfeld-io/monitoring",
"sebastian-sommerfeld-io/github-action-update-antora-yml",
"sebastian-sommerfeld-io/github-action-generate-readme",
"sebastian-sommerfeld-io/docker-image-tf-graph-beautifier",
"sebastian-sommerfeld-io/docker-image-jq",
"sebastian-sommerfeld-io/docker-image-git",
"sebastian-sommerfeld-io/docker-image-ftp-client",
"sebastian-sommerfeld-io/docker-image-folderslint",
"sebastian-sommerfeld-io/docker-image-adoc-antora",
"sebastian-sommerfeld-io/trashbox",
"sebastian-sommerfeld-io/provinzial",
])
I guess I don't get the for_each stuff right. Can anyone help me? I want to query all my repos taht fit my criteria and add labels to them.
UPDATE: I just detected that with my static approach I pass id, not full_name. I updated my code to this (snippet from above):
resource "github_issue_label" "bug" {
for_each = data.github_repositories.repos.repo_ids
repository = each.value
name = "bug"
description = "Something is not working"
color = "B60205"
}
Now at least the error message is different:
│ Error: Invalid for_each argument
│
│ on issues.tf line 2, in resource "github_issue_label" "bug":
│ 2: for_each = data.github_repositories.repos.repo_ids
│ ├────────────────
│ │ data.github_repositories.repos.repo_ids is list of number with 18 elements
│
│ The given "for_each" argument value is unsuitable: the "for_each" argument
│ must be a map, or set of strings, and you have provided a value of type
│ list of number.

How to select owner while creating Github repository using terraform

I can create Github repository in an organisation as well as under my personal namespace.
I'm trying to create repository under org with terraform https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository
But it creates the repository under my personal namespace. The token I am using is authorized to create repository under the org. How do I specify the owner/org for the repo?
Putting name as org/repoName does not seem to work.
resource "github_repository" "new-repo" {
name = "org/sos-repo"
private = true
}
I believe the organisation is considered the owner input on the provider configuration.
This setting used to be called "organization".
You can specify it in the provider configuration block or you can use an environment variable GITHUB_OWNER.
e.g.
provider "github" {
owner = "my-org"
app_auth {
id = var.app_id # or `GITHUB_APP_ID`
installation_id = var.app_installation_id # or `GITHUB_APP_INSTALLATION_ID`
pem_file = var.app_pem_file # or `GITHUB_APP_PEM_FILE`
}
}
resource "github_membership" "membership_for_user_x" {
# ...
}
resource "github_repository" "example" {
name = "example"
description = "My awesome codebase"
visibility = "public"
}

Isolation between kubernetes.admission policies in OPA

I use the vanilla Open Policy Agent as a deployment on Kubernetes for handling admission webhooks.
The behavior of multiple policies evaluation is not clear to me, see this example:
## policy-1.rego
package kubernetes.admission
check_namespace {
# evaluate to true
namespaces := {"namespace1"}
namespaces[input.request.namespace]
}
check_user {
# evaluate to false
users := {"user1"}
users[input.request.userInfo.username]
}
allow["yes - user1 and namespace1"] {
check_namespace
check_user
}
.
## policy-2.rego
package kubernetes.admission
check_namespace {
# evaluate to false
namespaces := {"namespace2"}
namespaces[input.request.namespace]
}
check_user {
# evaluate to true
users := {"user2"}
users[input.request.userInfo.username]
}
allow["yes - user2 and namespace12] {
check_namespace
check_user
}
.
## main.rego
package system
import data.kubernetes.admission
main = {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": response,
}
default uid = ""
uid = input.request.uid
response = {
"allowed": true,
"uid": uid,
} {
reason = concat(", ", admission.allow)
reason != ""
}
else = {"allowed": false, "uid": uid}
.
## example input
{
"apiVersion": "admission.k8s.io/v1beta1",
"kind": "AdmissionReview",
"request": {
"namespace": "namespace1",
"userInfo": {
"username": "user2"
}
}
}
.
## Results
"allow": [
"yes - user1 and namespace1",
"yes - user2 and namespace2"
]
It seems that all of my policies are being evaluated as just one flat file, but i would expect that each policy will be evaluated independently from the others
What am I missing here?
Files don't really mean anything to OPA, but packages do. Since both of your policies are defined in the kubernetes.admission module, they'll essentially be appended together as one. This works in your case only due to one of the check_user and check_namespace respectively evaluating to undefined given your input. If they hadn't you would see an error message about conflict, since complete rules can't evalutate to different results (i.e. allow can't be both true and false).
If you rather use a separate package per policy, like, say kubernetes.admission.policy1 and kubernetes.admission.policy2, this would not be a concern. You'd need to update your main policy to collect an aggregate of the allow rules from all of your policies though. Something like:
reason = concat(", ", [a | a := data.kubernetes.admission[policy].allow[_]])
This would iterate over all the sub-packages in kubernetes.admission and collect the allow rule result from each. This pattern is called dynamic policy composition, and I wrote a longer text on the topic here.
(As a side note, you probably want to aggregate deny rules rather than allow. As far as I know, clients like kubectl won't print out the reason from the response unless it's actually denied... and it's generally less useful to know why something succeeded rather than failed. You'll still have the OPA decision logs to consult if you want to know more about why a request succeeded or failed later).

Terraform Error creating Topic: googleapi: Error 403: User not authorized to perform this action

Googleapi: Error 403: User not authorized to perform this action
provider "google" {
project = "xxxxxx"
region = "us-central1"
}
resource "google_pubsub_topic" "gke_cluster_upgrade_notifications" {
name = "cluster-notifications"
labels = {
foo = "bar"
}
message_storage_policy {
allowed_persistence_regions = [
"region",
]
}
}
# create the storage bucket for our scripts
resource "google_storage_bucket" "source_code" {
name = "xxxxxx-bucket-lh05111992"
location = "us-central1"
force_destroy = true
}
# zip up function source code
data "archive_file" "function_script_zip" {
type = "zip"
source_dir = "./function/"
output_path = "./function/main.py.zip"
}
# add function source code to storage
resource "google_storage_bucket_object" "function_script_zip" {
name = "main.py.zip"
bucket = google_storage_bucket.source_code.name
source = "./function/main.py.zip"
}
resource "google_cloudfunctions_function" "gke_cluster_upgrade_notifications" {---
-------
}
The service account has the owner role attached
Also tried using
1.export GOOGLE_APPLICATION_CREDENTIALS={{path}}
2.credentials = "${file("credentials.json")}" by place json file in terraform root folder.
It seems that the used account is missing some permissions (e.g. pubsub.topics.create) to create the Cloud Pub/Sub topic. The owner role should be sufficient to create the topic, as it contains the necessary permissions (you can check this here). Therefore, a wrong service account might be set in Terraform.
To address these IAM issues I would suggest:
Use the Policy Troubleshooter.
Impersonate service account and do the API call using CLI with --verbosity=debug flag, which will provide helpful information about the missing permissions.

Create Azure AD Application using terraform(Invalid UUID)

I am using terraform to create an Azure AD application, I have tried the default example from the terraform samples https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application also I have customized the code below from the one I created manually(basically, I have created an application manually in AD and got the details from the data resource using terraform for the created application). both the code throws same error
Error: Value must be a valid UUID │ │ with
azuread_application.example, │ on adapp.tf line 3, in resource
"azuread_application" "example": │ 3: resource
"azuread_application" "example" {
This is the code I have customized from the original example
data "azuread_client_config" "current" {}
resource "azuread_application" "example" {
display_name = "example"
identifier_uris = ["api://example-app"]
owners = [data.azuread_client_config.current.object_id]
sign_in_audience = "AzureADMultipleOrgs"
required_resource_access {
resource_app_id = "00000003-0000-0000-c000-000000000000"
resource_access {
id = "..."
type = "Scope"
}
}
web {
redirect_uris = ["https://app.example.net/account"]
implicit_grant {
access_token_issuance_enabled = false
}
}
}
I have validated the "data.azuread_client_config.current.object_id", its not null and it producing the value.
Terraform Config:
Terraform v0.15.4 on windows_amd64
provider registry.terraform.io/hashicorp/azuread v1.6.0
As you are using the resource app id of "Microsoft Graph" (00000003-0000-0000-c000-000000000000) , so you have to provide what delegated permissions you need for your app to have in Microsoft graph like User.read etc.
Some CLI commands that will help you to get the Microsoft Graph resource App Id's and Delegated Permissions Id's:
- az ad sp list --display-name "Microsoft Graph" --query
'[].{appDisplayName:appDisplayName, appId:appId}'
--output table
- az ad sp show --id 00000003-0000-0000-c000-000000000000 --query
"oauth2Permissions[].{Value:value, Id:id}" --output table
So as you are already using the default Microsoft Graph App Id , we need to get the delegated permission ID's to provide in resource access id.
Then your terraform code will be as below :
data "azuread_client_config" "current" {}
resource "azuread_application" "example" {
display_name = "example"
identifier_uris = ["api://example-app"]
owners = [data.azuread_client_config.current.object_id]
sign_in_audience = "AzureADMultipleOrgs"
required_resource_access {
resource_app_id = "00000003-0000-0000-c000-000000000000"# resourceid of microsoft graph
resource_access {
id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" # User.Read
type = "Scope"
}
}
web {
redirect_uris = ["https://app.example.net/account"]
implicit_grant {
access_token_issuance_enabled = false
}
}
}
Doing a terraform plan :
Note : Default Microsoft Graph App ID is "00000003-0000-0000-c000-000000000000" and Default Windows Active Directory App ID (Azure AD Graph) is "00000002-0000-0000-c000-000000000000". Based on your requirement you can use Microsoft Graph or Azure AD Graph .