jason-johnson / azure-pipelines-tasks-terraform

Azure Pipelines extension for Terraform
MIT License
125 stars 53 forks source link

Terraform init task not masking Service Connection secret #336

Closed darrens280 closed 1 year ago

darrens280 commented 1 year ago

When using an Azure yaml pipeline to perform Terraform init task (which calls from a yaml template file), the Azure Service Connection secret is getting displayed as plain text in the pipeline job results.

To Reproduce Steps to reproduce the behavior:

  1. Setup pipeline as
    steps:
    - task: TerraformCLI@0
    displayName: "terraform init"
    inputs:
      command: "init"
      workingDirectory: $(workingDirectory)
      backendType: "azurerm"
      ensureBackend: false
      backendServiceArm: $(azureDevOpsServiceConnectionName)
      backendAzureRmResourceGroupName: $(backend-resource-group)
      backendAzureRmStorageAccountName: $(backend-storage-account)
      backendAzureRmContainerName: $(backend-blob-container)
      backendAzureRmKey: $(statefile)
      allowTelemetryCollection: $(useTelemetery)
  2. Execute pipeline
  3. See error (scroll to the right) - The client_id value is masked (which is expected behaviour), but the client_secret value is not.
    
    Starting: terraform init
    ==============================================================================
    Task         : Terraform CLI
    Description  : Execute terraform cli commands
    Version      : 0.7.8
    Author       : Charles Zipp
    Help         : 
    ==============================================================================
    C:\Tools\terraform.exe version
    Terraform v1.3.8
    on windows_amd64

Your version of Terraform is out of date! The latest version is 1.3.9. You can update by downloading from https://www.terraform.io/downloads.html C:\Tools\terraform.exe init -backend-config=storage_account_name= -backend-config=container_name= -backend-config=key=.terraform.tfstate -backend-config=resource_group_name= -backend-config=subscription_id= -backend-config=tenant_id= -backend-config=client_id=*** -backend-config=client_secret=supersecretpassword Initializing modules... ...


**Expected behavior**
`client_id` and `client_secret` values should be masked in the job log output/results screen of the ADO Agent pipeline run.

**Agent Configuration**
 - OS: Windows 2019 Server
 - Self Hosted
 - Terraform version used = 1.3.8
 - AzureCLI version used = 2.45.0

**Additional context**
There is a token replacement templates that runs immediately before the Terraform init task. The INIT initializes the backend storage in Azure Storage Account with relevant Service Principal credentials, and prepares to run Terraform plan. The INIT does succeed just fine, but I just want to know why the secret is not getting masked in some Stages of my pipeline, but in the next Stage of the pipeline it does get masked, when both Stages are referencing the same `terraform_init.yml` template file. 
jason-johnson commented 1 year ago

I cannot reproduce. I just reran a pipeline using the latest terraformCLI task and both client_id and client_secret are replaced by stars. I also checked the smoke tests, which use another method for the init setup and they also hide both.

darrens280 commented 1 year ago

Hi. Thanks for checking. Not sure if this helps or not, but if I expand a little more, the masking seems to happen correctly whenever the target environment is Production (ie. environment='prd'), however if it's DEV, then the masking does not happen correctly on the INIT job, and I get the output as described in my original post above... I am using the exact same terraform_init.yaml template for all environments, all getting called the same way from each Stage - just with differing values for backend storage account/container etc

Here is my terraform_init.yaml template:


steps:
  # Run with PRD service connection.
  - task: TerraformCLI@0
    condition: eq(variables.environment, 'prd')
    displayName: "terraform init"
    inputs:
      command: "init"
      workingDirectory: $(workingDirectory)
      backendType: "azurerm"
      ensureBackend: false
      backendServiceArm: $(azureDevOpsServiceConnectionNamePrd)
      backendAzureRmResourceGroupName: $(backend-resource-group)
      backendAzureRmStorageAccountName: $(backend-storage-account)
      backendAzureRmContainerName: $(backend-blob-container)
      backendAzureRmKey: $(statefile)
      allowTelemetryCollection: $(useTelemetery)

  # Run with DEV service connection.
  - task: TerraformCLI@0
    condition: eq(variables.environment, 'dev')
    displayName: "terraform init"
    inputs:
      command: "init"
      workingDirectory: $(workingDirectory)
      backendType: "azurerm"
      ensureBackend: false
      backendServiceArm: $(azureDevOpsServiceConnectionNameDev)
      backendAzureRmResourceGroupName: $(backend-resource-group)
      backendAzureRmStorageAccountName: $(backend-storage-account)
      backendAzureRmContainerName: $(backend-blob-container)
      backendAzureRmKey: $(statefile)
      allowTelemetryCollection: $(useTelemetery)      
jason-johnson commented 1 year ago

If this is all that's happening then the only difference is the service connection. Are the two service connections created exactly the same way? Something is making Azure Devops not understand that the secret is a sensitive variable and since the only thing that is different is the service connection, it has to be coming from there somehow.

darrens280 commented 1 year ago

Hi. Thanks for the suggestion. I have compared both DEV & PROD Service Connections, and every field/attribute/setting is exactly the same, both in the Azure DevOps interface, and in the Azure Web Portal (under Enterprise Applications). Both have valid secrets etc. I also checked the info provided by PowerShell cmdlet Get-AzureADServicePrincipal and all is the same (except for IDs and friendly display names of course).

I also tried taking the Terraform Init template call/s out of the pipeline, and making it an inline task instead, for each DEV and PROD Stage, but I still get the same results: DEV secret is not masked, but PROD secret is masked.

I will keep trying different combinations...

Thanks

jason-johnson commented 1 year ago

Hi @darrens280, any update?

darrens280 commented 1 year ago

Sadly I am still having the same experience as before. The pipeline, when running DEV, shows the Service Connection secret, but PROD masks the password as expected, and they are using the same Terraform templates, with an IF statement to use the one of the two Service Connections based on an environment variable.

Today, I see that our Azure DevOps has updated the "Azure Pipelines Terraform Tasks" extension, from Charles Zipp's last version of 0.7.12, to your "Jason Johnson" latest version 1.0.2 (see screenshot below)

I can confirm our self-hosted ADO Agents have been refreshed, and the Microsoft.Azure.DevOps.Pipelines.Agent Extension (v1.31) successfully installed of part of their VM Scale Set configuration. However, when I run a pipeline again, it still downloads the Charles Zipp, 0.7.12 version and not your 1.0.2, and continues to display the Service Connection secret in the pipeline logs.

How do I force ADO or the ADO Agents to consume the latest available Azure Pipelines CLI extension (1.0.2) from ADO?

I tried updating the YAML to this, which does then downloaded v1.0.2, but still in the pipeline logs, it still shows as Charles Zipp, 1.0.2, and still shows the secret in plain text #sadtimes

- task: TerraformCLI@1

Thanks Darren

image

image

jason-johnson commented 1 year ago

You will probably need to uninstall the Charles Zipp version manually.

jason-johnson commented 1 year ago

Hi @darrens280 , any update?

darrens280 commented 1 year ago

Hi @jason-johnson

Update from here is....

...same result unfortunately, even with latest version of the extension v1.0.5. The Service Connection secret is displayed in plain text in the Azure Pipeline logs, on the end of the terraform init command (eg. end of line 15 in screenshot below)

The author in the JSON file (from the Azure DevOps Agent) for v1.0.5 still reads "Charles Zipp" - not sure if this is intentional or not?

image

image

image

Thanks

jason-johnson commented 1 year ago

Hi

Charles was the author so I'm leaving that unless we reach a point that there's nothing really left of his code.

In any case, this is quite a tricky one. I can't reproduce it, none of our tests have this issue so I'm not sure what is triggering it.

darrens280 commented 1 year ago

Thank you for your efforts in trying to reproduce & resolve this issue.

I have done more comparisons between the DEV and PROD Service Connections in Azure DevOps, and in AzureAD, as well as comparing the YAML pipeline syntax referencing the YAML template that runs the tasks (using the brand new v1.0.5 TerraformCLI extension version). Tests reveal the same result, where the PROD Service Connection's secret is masked during the terraform init task, but the DEV secret sadly is not masked.

Can't help but think the issue is local to our environment / setup.

I will keep trying to resolve, but in the meantime I will close this issue.

Thanks again