aws_role_arn not being used for Terraform Vault provider in auth_login_aws - terraform

I'm hoping to contribute some documentation on auth_login_aws because I'm trying to use the feature described in this feature request.
TL;DR, despite specifying aws_role_arn in the snippet below, the provider is still trying to use the credentials in my ~.aws/credentials file.
provider "vault" {
address = "http://127.0.0.1:8200"
skip_child_token = true
auth_login_aws {
role = "dev-role-corey-iam"
header_value = "vault.example.com"
aws_role_arn = "arn:aws:iam::1234567890:role/SomeRole"
aws_role_session_name = "corey-test-session"
aws_region = "us-east-1"
}
}
I'm just trying to get Terraform to pull secrets from a local Vault instance using the SomeRole IAM role, which is bound to the dev-role-corey-iam Auth role. I'm currently logged in with a different AWS IAM role (PrimaryRole) that can assume the SomeRole role. When I run terraform plan, the provider is using PrimaryRole instead when I expect it to use SomeRole because aws_role_arn is specified. Am I misinterpreting what aws_role_arn should do?

Related

How to create a google cloud pubsub pull subscriptions with service account by terraform?

In the terraform documentation for google_pubsub_subscription, it mentions having a oidc_token property under push_configuration allows push-subscriptions to utilise service account, but has no mention on how to use service account for pull subscriptions.
How can explicitly set the service account to be used during the creation of pull pubsub subscriptions?
I tried adding oidc_token block, but it did not worked as it does not expect that block directly.
Scenario:
I have a service account that has access to pubsub topics (and necessary permissions to attach subscriptions to it) in Project_A
I want to create subscriptions to those topics in Project_B in terraform.
I need to explicitly use this service account in terraform, so i can create subscriptions at Project_B to topics of Project_A
google_cloudfunctions_function resource for example, has field called service_account_email for setting service account. But there is no for google_pubsub_subscription resource, for pull subscriptions in the documentation.
Actually the service account (service_account_email)is to be specified inside the oidc_token section (you had to go further in the doc :)
below a working example of a push sub with service account and audience (optional)
variable "project" {
type = string
default = "<YOUR_PROJECT_ID>"
}
resource "google_pubsub_topic" "example" {
project = var.project
name = "example-topic"
}
resource "google_pubsub_subscription" "example" {
project = var.project
name = "example-push-subscription-with-auth"
topic = google_pubsub_topic.example.name
push_config {
push_endpoint = "https://example.com/push"
oidc_token {
service_account_email = "${var.project}#appspot.gserviceaccount.com"
audience = "https://example.com/push"
}
}
}
I'm adding another answer, because the question has changes a lot after the different comments.
Here is the solution with the hypothesis that a topic topic-sof is already created in a different project than the subscription one.
I created a service account (SA) on the subscription project, I called it stackoverflow-playground-sa and gave it only the Pub/Sub Editor role as you can see in the screenshot below.
I gave the SA the the Pub/Sub Subscriber role on the topic topic-sof s show in the screenshot below.
If you don't do this step: you will get this error at terraform apply
Error: Error creating Subscription: googleapi: Error 403: User not authorized to perform this action
Of course I did the 2 first steps of roles assignments with a user with enough permissions on both projects
I created a json key file for my SA and downloaded under /path/to/my/key/stackoverflow-playground-sa.json
I authenticated as my SA using
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/key/stackoverflow-playground-sa.json
So it can be used by terraform to create the subscription.
Here is the terraform configuration to create the subscription
variable "subscription_project" {
type = string
default = "<YOUR_SUBSCRIPTION_PROJECT>"
}
variable "topic_project" {
type = string
default = "<YOUR_TOPIC_PROJECT>"
}
resource "google_pubsub_subscription" "pull-subscription-of-topic-in-another-project" {
project = var.subscription_project
name = "pull-subscription-of-topic-in-another-project"
topic = "projects/${var.topic_project}/topics/topic-sof"
}
Run (apply) my terraform and the subscription pull-subscription-of-topic-in-another-project is created and attached to the topic topic-sof in the other project.
I published a message to topic-sof using the web ui.
Still authenticated as stackoverflow-playground-sa thanks to step 4, I pull the message (using gcloud in my treminal) et voilĂ  message received:
To summarise : there is no service account to specify in your terraform configuration for this requirement. The service account and its key is set outside terraform (step 1 to 4) so that terraform process can be authenticated and authorised to create the resources configured (the subscription in our case).
Besides using a service account key as in step 4 is a bad practice, security wise. An alternative is to use
gcloud auth application-default login
That will let you set the default credentials of the user of your choice. Given that user has the roles I set for the SA.

Permission iam.serviceAccounts.setIamPolicy is required to perform this operation on service account

I have below terraform script, to create a new service account and make it owner.
The scripts creates the service account, but it will throw an error on assigning role
resource "google_service_account" "pci_api_service_account" {
account_id = "pci-api"
display_name = "Api"
project = var.project_id
}
resource "google_service_account_iam_member" "pci_api_owner_binding" {
# service_account_id = "projects/pcb-poc-pci/serviceAccounts/infra-admin-sa#pcb-poc-pci.iam.gserviceaccount.com"
service_account_id = google_service_account.pci_api_service_account.name
role = "roles/owner"
member = "serviceAccount:${google_service_account.pci_api_service_account.email}"
depends_on = [
google_service_account.pci_api_service_account
]
}
and I already autheticated with infra-admin-sa service account by running
gcloud auth activate-service-account --project=pcb-poc-pci --key-file ~/sa/pcb-poc-pci-test-sa-94aa6c81d650.json
When I run terragrunt apply I get this error for the second script
Error: Error applying IAM policy for service account 'projects/pcb-poc-pci/serviceAccounts/pci-api#pcb-poc-pci.iam.gserviceaccount.com': Error setting IAM policy for service account 'projects/pcb-poc-pci/serviceAccounts/pci-api#pcb-poc-pci.iam.gserviceaccount.com': googleapi: Error 403: Permission iam.serviceAccounts.setIamPolicy is required to perform this operation on service account projects/pcb-poc-pci/serviceAccounts/pci-api#pcb-poc-pci.iam.gserviceaccount.com., forbidden
These are the Roles of that service account
Based on google doc here and the error message, Service Account Admin should be enough, which my service account already have
Not sure what I missed
Solution 1
seems command line was not picking the correct credential/service account although I used gcloud auth activate-service-account command.
so I added this to my script
provider "google" {
credentials = file(var.service_account_file_path)
project = var.project_id
}
and now it's working fine
Solution 2
as per #John Hansley comments below
export GOOGLE_APPLICATION_CREDENTIALS=fullpath.json
then terraform will be picking that service account file and scripts will run successfully.
This method is preferred since less issue in CICD pipeline and other deveopers, to set terraform variables

Terraform - Trying to create resources in Azure with service principal and pulling that SPs key from keyvault

Been brushing up using Terraform to manage resources in Azure the past week or so.
Great tool.
I've found there is a distinction between using an AZ user account vs service principal.
The goal is to create resources in Azure using a designated service principal and referencing it's secret that is stored within AZ key vault. Moving away from locally stored secret (file, env var, etc).
I can successfully create resources using an authenticated service principal as long as I have my azurerm provider containing the subid, clientid, clientsecret & tenantid, it works great.
Example of what works when I store service principal secret as a var sp_secret in variables.tf (or even works as env var):
provider "azurerm" {
version = "=2.48.0"
features { }
subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_secret = "${var.sp_secret}"
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
I have been able to successfully pull the service principals secret from the keyvault and 'output' it, but what I want to do is to pull that secret from kv and use, say as a var inside the provider client_secret value. ex. 'client_secret = "${link to secret sitting in kv}"'
Here is what I am doing to retrieve the SP secret from keyvault and output it:
data "azurerm_client_config" "current" {}
variable "keyvault_name" {
default = "blah-kv"
}
variable "kvrg_name" {
default = "blah-kv-rg"
}
data "azurerm_key_vault" "keyvault" {
name = "${var.keyvault_name}"
resource_group_name = "${var.kvrg_name}"
}
data "azurerm_key_vault_secret" "kv-sp" {
name = "blah-tf-sp-secret"
key_vault_id = "${data.azurerm_key_vault.keyvault.id}"
}
output "secret_value" {
value = "${data.azurerm_key_vault_secret.kv-sp.value}"
}
As mentioned, the above snippet successfully retrieves and outputs the secret. I just want to, instead of output the secret, just set that secret as client_secret value in the azurerm provider reference.
I've tried many variations of client_secret = "${data.azurerm_key_vault_secret.kv-sp.value}", and I get the following error:
Error: Cycle: data.azurerm_key_vault.keyvault, provider["registry.terraform.io/hashicorp/azurerm"], data.azurerm_key_vault_secret.kv-sp
I interpret the above error to indicate a circular reference. I've tried a few things i've picked up in my searching for an answer, but no dice.
Any guidance is appreciated.
Thanks!
As I know. it's impossible to achieve what you expect. When you use the Terraform to manage Azure resources, then you need to have an Azure account or service principle with enough permissions. If you use the service principle, it means you need to configure the provider azurerm with client id and client secret before running the Terraform code. But when you store the secret in the Azure Key Vault, then you need to run the code, and then you can get the secret. It causes cycle dependency.

Service Principal from Terraform not recognized by Azure API

What specific changes need to be made to the syntax below in order for the terraform azurerm provider to be able to authenticate the service principal that will be created using the following code?
The Problem
A Second Terraform module needs to authenticate to Azure through the azurerm provider with a client_id and client_secret that is created programatically during an earlier, separate process.
The provider block in the Second Terraform module looks like:
provider "azurerm" {
subscription_id = var.subscriptionId
client_id = var.clientId
client_secret = var.clientSecret
tenant_id = var.tenantId
}
The problem arises when the correct values whcih we validated from the earlier preceding process are not accepted as the var.clientId and the var.clientSecret in the provider code block above.
How the Service Principal is Created:
The client_id and client_secret to be used to authenticate to the Second Terraform module are currently created by a First Terraform module which includes the following:
resource "azuread_application" "appReg" {
name = var.appName
}
resource "azuread_service_principal" "example-sp" {
application_id = azuread_application.appReg.application_id
}
resource "azuread_service_principal_password" "example-sp_pwd" {
service_principal_id = azuread_service_principal.example-sp.id
value = "long-random-string"
end_date = "2021-06-02T01:02:03Z"
}
data "azurerm_subscription" "thisSubscription" {
subscription_id = var.subscriptionId
}
resource "azurerm_role_assignment" "example-sp_role_assignment" {
scope = data.azurerm_subscription.thisSubscription.id
role_definition_name = "Contributor"
principal_id = azuread_service_principal.example-sp.id
}
resource "azuread_application_app_role" "example-role" {
application_object_id = azuread_application.appReg.id
allowed_member_types = ["User", "Application"]
description = "Admins can manage roles and perform all task actions"
display_name = "Admin"
is_enabled = true
value = "administer"
}
Terraform reports Apply complete after the above First module is run, and we are also able to confirm in the Azure Portal that the correct Active Directory has a new app registration with name var.appName and with ID equal to what we find in the First modules tfstate file.
The Error Message:
When Terraform tries to apply the Second module using the Service Principal ID and Secret created by the First module, the following error is thrown:
Error:
Error building account:
Error getting authenticated object ID:
Error listing Service Principals:
autorest.DetailedError{
Original:adal.tokenRefreshError{
message:"adal: Refresh request failed.
Status Code = '400'.
Response body: {
\"error\":\"unauthorized_client\",
\"error_description\":\"AADSTS700016:
Application with identifier 'correct-app-id' was not found in the directory 'the-right-ad-id'.
This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant.
You may have sent your authentication request to the wrong tenant.\\r\\n
Trace ID: some-trace-id\\r\\n
Correlation ID: correlation-id-redacted\\r\\n
Timestamp: 2020-12-31 19:02:19Z\",
\"error_codes\":[700016],
\"timestamp\":\"2020-12-31 19:02:19Z\",
\"trace_id\":\"some-trace-id\",
\"correlation_id\":\"correlation-id-redacted\",
\"error_uri\":\"https://login.microsoftonline.com/error?code=700016\"
}",
resp:(*http.Response)(0xc000ac2000)},
PackageType:"azure.BearerAuthorizer",
Method:"WithAuthorization",
StatusCode:400,
Message:"Failed to refresh the Token for request to https://graph.windows.net/the-right-ad-id/servicePrincipals?%24filter=appId+eq+%27correct-app-id%27&api-version=1.6",
ServiceError:[]uint8(nil),
Response:(*http.Response)(0xc000ac2000)
}
The error message does not seem helpful because we validated that the app is registered with the AAD instance.
How can we resolve this problem and programmatically create a client_id and client_secret that will be accepted and usable by the Second module?
As I see there is no problem with your Terraform code. It should work fine. But you got the error that the application was not found in the tenant. So what you need to do is to check if the tenant Id is really right in the second module.
I had the same issue on Deployment Agents for Terraform on Kubernetes. Several types of error can appear when the memory or the CPU is not large enough.
Below are the Terraform recommendations: https://www.terraform.io/docs/enterprise/before-installing/index.html
You have to be careful with the deployment infrastructures that mutualises the resources (K8s, Hypervisor Pool etc.)when several Terraform deployments are in parallel it causes somewhat random errors.
Terraform which does not stop, API AZure / AWS error, tfstate lock etc.

How can I fake cloud provider credentials in Terraform?

I am writing a cross-cloud Terraform module (for google and aws) which accepts a cloud input variable and applies it accordingly, for example:
variable "cloud" {}
resource "google_example" {
count = "${var.cloud == "google" ? 1 : 0}"
}
resource "aws_example" {
count = "${var.cloud == "aws" ? 1 : 0}"
}
The problem with this approach is that I only want to provide credentials for the selected cloud, not both - setting cloud=aws for example I get:
Error: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
Is there any way to fake cloud provider credentials for the non-selected cloud, or do I need to implement some sort of Terraform templating?
Every resource can allow a provider field, which tells it which provider to use. I think you may need to set real credentials for both providers, else Terraform will fail to initialise if you use backend configuration. Otherwise, set fake credentials as environment variables and see what happens:
TF_VAR_AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" # et cetera

Resources