Automating YAML Documentation Compliance

Automating YAML Documentation Compliance

Automating YAML Documentation Compliance

Azure devops with neo

Automating YAML Documentation Compliance

Introduction

In modern Software Development & DevOps world, maintaining clear and consistent documentation within configuration files is essential. YAML files, often used for defining workflows, configurations, and pipelines, should start with a comment block that describes their purpose. This practice not only enhances readability but also aids in onboarding new team members and maintaining the project over time.

In this blog post, I will dive into how you can automate the validation of YAML files to ensure they start with a mandatory documentation block using Azure DevOps pipelines and a custom PowerShell script.

Its important to note that this pipeline will run as a gated check-in for Azure DevOps Pull Requests, you can configure gated check in to run only for file suffixes such as *.yml & *.yaml

AZURE DEVOPS directory structure

Ensuring that all YAML files in a project start with a documentation block can be a tedious manual process. Automating this check not only saves time but also enforces consistency across the project. By integrating this validation into your Azure DevOps pipeline, you can catch non-compliant files early in the development process.

Solution Directory Structure

First, let’s have a look at how I organized the directory structure for the solution.

 

  • yamlValidation (folder):

    • The root folder for the project focuses on YAML validation.
  • scripts (subfolder):

    • This folder contains scripts related to the project. In this case, there is one PowerShell script for YAML comment validation.
  • Test-YamlComment.ps1 (PowerShell Script):

    • A PowerShell script file that contains functionality for validating that YAML files start with a comment block. 
  • azure-pipelines.yaml (Pipeline file):

    • File represents the Azure DevOps pipeline definition in YAML. It’s used to define the build, validation, or release process; in my case, it runs the Test-YamlComment.ps1 script to validate the YAML files.
  • README.md:

    • The markdown file contains documentation that explains the whole project. 

 

AZURE DEVOPS PIPELINE CONFIGURATION

The pipeline is defined in the azure-pipelines.yaml file. This configuration sets up the environment and specifies the steps required to execute the validation script.

Below is the content of the azure-pipelines.yaml file

				
					pool:
  vmImage: windows-latest

trigger:
  branches:
    include:
    - '*'
    exclude:
    - main
    - master

steps:
    - checkout: self
      persistCredentials: true

    - task: PowerShell@2
      inputs:
        filePath: $(Build.SourcesDirectory)/Solutions/yamlValidation/scripts/Test-YamlComment.ps1
        arguments: >
          -GitEmail 'neo@neopyon.io'
          -GitUserName 'Neo Jovic'
        pwsh: true
      displayName: Test if YAML files start with documentation block
				
			

Let’s now break down the parts of the pipeline into concise explanations:

  • Pool Configuration:

    • vmImage: windows-latest specifies the agent image which will run the pipeline tasks, in this case, it will use Microsoft hosted agents.
  • Trigger Settings:

    • Branches:
      • include: '*' – includes all branches.
      • exclude: main, master – excludes the main and master branches from triggering the pipeline.
  • Steps:

    • Checkout Step:
      • checkout: self – checks out the code repository.
      • persistCredentials: true – retains the credentials for any Git operations in subsequent steps.
    • PowerShell Task:
      • task: PowerShell@2 – specifies the use of the PowerShell task version 2.
      • Inputs:
        • filePath – points to the validation script Test-YamlComment.ps1.
        • arguments – passes the Git user email and username as parameters to the script.
        • pwsh: true – runs the script with PowerShell Core.
      • displayName – provides a readable name for the task in the pipeline UI.

POWERSHELL SCRIPT FOR YAML VALIDATION

Now, let’s understand the crucial part of the Pipeline, the PowerShell script that does the YAML file validation.

				
					[CmdletBinding()]
param (
    [Parameter(Mandatory)]
    [string]$GitEmail,

    [Parameter(Mandatory)]
    [string]$GitUserName
)
process {
    $branch = $env:System_PullRequest_SourceBranch -replace 'refs/heads/', ''
    git config user.email $GitEmail > $null 2>&1
    git config user.name $GitUserName > $null 2>&1
    git fetch origin > $null 2>&1
    git checkout $branch > $null 2>&1
    git fetch origin master:master > $null 2>&1
    $changedFiles = git diff --name-only master $branch
    $yamlFiles = $changedFiles | Where-Object { ($_ -notmatch 'azure-pipelines\.yml$' -and $_ -notmatch 'azure-pipelines\.yaml$') -and ($_.EndsWith('.yaml') -or $_.EndsWith('.yml')) }
    foreach ($file in $yamlFiles) {
        $firstLine = Get-Content $file -First 1
        if ($firstLine -notmatch '^#') {
            throw "YAML file - $file does not start with a comment/documentation."
        }
    }
}
				
			

Let’s now break down the parts of the PowerShell YAML Validation script into concise explanations.

  • Parameters:

    • $GitEmail and $GitUserName – are used to configure Git for operations within the script.
  • Process Block:

    • Branch Identification:
      • Retrieves the source branch name from the environment variables.
    • Git Configuration:
      • Configures Git with the provided email and username.
    • Fetching and Checking Out Branches:
      • Fetches the latest changes from the origin.
      • Checks out the current branch.
      • Fetches the master branch for comparison.
    • Identifying Changed YAML Files:
      • Uses git diff to find files changed between the master and current branch.
      • Filters out azure-pipelines.yml and azure-pipelines.yaml to prevent self-validation.
      • Filters for files ending with .yaml or .yml.
    • Validation Loop:
      • Iterates over each changed YAML file in the last update of the Pull Request.
      • Reads the first line of the file.
      • Checks if the first line starts with a # (comment indicator in YAML).
      • Throws an error if the file does not start with a comment.

How it works?

When a pull request is created or updated, the pipeline is triggered for the specified paths and branches. 

The PowerShell script performs the following actions:

  1. Git Setup: Configures Git with the provided user details to perform repository operations.

  2. Branch Comparison: Fetches the current and master branches to identify changes.

  3. File Identification: Determines which YAML files have been modified in the pull request, excluding the pipeline’s own YAML file.

  4. Validation: Checks each changed YAML file to ensure it starts with a comment. If a file does not comply, the script throws an error, causing the pipeline to fail.

  5. Feedback: The pipeline provides immediate feedback to the engineer, indicating which file failed the validation.

CONCLUSION

By integrating this validation step into your Azure DevOps pipeline, you automate the enforcement of documentation standards within your project. This approach ensures that all YAML configuration files start with a necessary documentation block, promoting better practices and aiding in project maintainability.

Key Takeaways:

  • Automating documentation checks saves time and enforces consistency.
  • Custom scripts can be integrated into Azure DevOps pipelines to extend functionality.
  • Immediate feedback in pull requests helps developers adhere to project standards.
Implementing such automation fosters a culture of quality and attention to detail within your development team. 
Start integrating similar checks today to enhance your project’s reliability and maintainability.

Automating YAML Documentation Compliance Read More »

Google Authentication with PowerShell

powershell with neo

google authentication with powershell

Introduction

I recently wanted to experiment with Google API and looked for the available module, but I haven’t found a good result. I had to make a first step myself, and that’s the authentication part. I spent a few hours on this subject and wrote a post.

GOOGLE AUTHENTICATION WITH POWERSHELL

First, you must provision the service account, grant it proper rights, and download the JSON-based credential file – this is out of the scope of this post; you can find plenty of information online.
The JSON file stores essential information to create the connection.
 
At the bottom of this page, you can find the function New-GoogleApiAccessToken. This function is a well-crafted masterpiece in PowerShell that effectively handles the complexities of creating a JSON Web Token (JWT) and exchanging it for an access token from Google API. This access token is the key that unlocks the door to a wide range of Google services, such as accessing files on Google Drive and managing calendars.
 
Now, let’s delve into how this script operates:
 
Input Parameters: The script requires a JSON string containing the service account credentials, the requested access scope from Google API, and, optionally, the user’s email address for whom the application is requesting delegated access.
 
JWT Generation: The script converts the JSON credentials into an object, extracts the necessary information, and constructs a JWT with a header and claim set. The claim set includes the issuer, scope, audience, expiration time, issued at the time, and subject (the target user’s email).
 
Digital Signature: Next, the script signs the JWT using the RSA SHA-256 algorithm to ensure that the token cannot be tampered with and can be verified by Google’s servers.
Token Exchange: After obtaining the JWT, the script sends a POST request to Google’s OAuth2 endpoint, which exchanges the JWT for an access token.
 
Access Token: Lastly, the script saves the access token in a variable that is specific to the script, ensuring its availability for authorized Google API calls.
 
How to use it?
If you plan to call Google APIs using PowerShell, you can place the function within your module and develop the other functions.
This function creates for you the authentication header, which you can refer to in all other functions by using the variable $script:authorizationHeader
You can always rewrite it to return the header instead of having it as the variable in the script scope.
 
The New-GoogleApiAccessToken script shows the power of automation and emphasizes the significance of secure and efficient API access. Whoever likes using PowerShell during their development, it’s a function you want handy.
 
Follow the white rabbit.
Neo
 
				
					function Get-GoogleApiAccessToken {
    <#
    .SYNOPSIS
    Retrieves an access token from Google API using a service account.

    .DESCRIPTION
    This function generates a JWT and exchanges it for an access token from Google API.
    The access token can then be used for authenticated requests to Google services.

    .PARAMETER GoogleAccessJson
    A JSON string containing the service account credentials including the private key and client email, this is the content of a file you downloaded as a Google credential file.

    .PARAMETER Scope
    The scope of access requested from Google API (e.g., 'https://www.googleapis.com/auth/drive').

    .PARAMETER TargetUserEmail
    The email address of the user for which the application is requesting delegated access (optional).

    .EXAMPLE
    $credentialsJson = Get-Content 'path/to/credentials.json' -Raw
    $accessToken = Get-GoogleApiAccessToken -GoogleAccessJson $credentialsJson -Scope 'https://www.googleapis.com/auth/drive.readonly'
    Use the $accessToken for authorized Google API calls.

    .NOTES
    Ensure that the service account has been granted the necessary permissions for the requested scope.
    #>
    param (
        [Parameter(Mandatory)]
        [string]$GoogleAccessJson, 

        [Parameter(Mandatory)]
        [string]$Scope,

        [Parameter(Mandatory)]
        [string]$TargetUserEmail 
    )
    $jsonContent = ConvertFrom-Json -InputObject $GoogleAccessJson -Depth 10
    $ServiceAccountEmail = $jsonContent.client_email
    $PrivateKey = $jsonContent.private_key -replace '-----BEGIN PRIVATE KEY-----\n' -replace '\n-----END PRIVATE KEY-----\n' -replace '\n'
    $header = @{
        alg = "RS256" 
        typ = "JWT" 
    }
    $headerBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($header | ConvertTo-Json))) 
    $timestamp = [Math]::Round((Get-Date -UFormat %s)) 
    $claimSet = @{
        iss   = $ServiceAccountEmail 
        scope = $Scope 
        aud   = "https://oauth2.googleapis.com/token" 
        exp   = $timestamp + 3600 
        iat   = $timestamp 
        sub   = $TargetUserEmail 
    }
    $claimSetBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($claimSet | ConvertTo-Json))) 
    $signatureInput = $headerBase64 + "." + $claimSetBase64 
    $signatureBytes = [System.Text.Encoding]::UTF8.GetBytes($signatureInput) 
    $privateKeyBytes = [System.Convert]::FromBase64String($PrivateKey) 
    $rsaProvider = [System.Security.Cryptography.RSA]::Create() 
    $bytesRead = $null
    $rsaProvider.ImportPkcs8PrivateKey($privateKeyBytes, [ref]$bytesRead) 
    $signature = $rsaProvider.SignData($signatureBytes, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1) 
    $signatureBase64 = [System.Convert]::ToBase64String($signature) 
    $jwt = $headerBase64 + "." + $claimSetBase64 + "." + $signatureBase64 
    $body = @{
        grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer" 
        assertion  = $jwt 
    }
    $response = Invoke-RestMethod -Uri "https://oauth2.googleapis.com/token" -Method POST -Body $body -ContentType "application/x-www-form-urlencoded" 
    $script:authorizationHeader = @{Authorization = 'Bearer {0}' -f $response.access_token }
}
				
			

Google Authentication with PowerShell Read More »

Scroll to Top