Skip to content

v0.4.0 Lab 2 Setup environment

CARMLPipelinePrincipal edited this page Dec 16, 2022 · 2 revisions

In this lab, you will set up CARML in your own environment. This set-up can be used to mimic the module factory you can use at your customer, or to perform end to end testing for contribution purposes.

Navigation


Step 1 - Create Azure Service Principal and Configure Access to Subscription

CARML tests the deployments and stores the module artifacts in an Azure subscription. To do so, it requires a service principal with access to it.

This step will guide you in the creation of the Service Principal and the gathering of the required values that will be used in the next steps.

For this lab, it is enough to just write them temporarily in for example Notepad. You should have notes for the following pieces:

  • Application (Client) ID
  • Service Principal Object ID (not the object ID of the application)
  • Service Principal Secret (password)
  • Tenant ID
  • Subscription ID
  • Parent Management Group

There are two alternatives to execute this step. Please, choose the one you prefer and move to Step 2 afterwards.

Alternative 1: Via the Az CLI

The following commands will allow us to:

  • Login to Azure using Az CLI.
  • Create a new Service Principal.
  • Assign the Service Principal Owner role at subscription level.

If you don't want to give Owner permissions to the Service Principals other options will be provided as part of the steps.

  1. As a first step, in the Azure portal, you have to navigate to your subscription by using for example the search bar on the top.

    Subscription search
  2. Following, select the subscription you want to grant access to. In the below example it is called Visual Studio Enterprise Subscription.

    Subscription select
  3. This brings you to the overview of your subscription. Here you need to perform 2 tasks:

    • Make note of the Subscription ID for later reference
    • Make note of the Parent Management Group for later reference

    Subscription overview

  4. Navigate back to your local Visual Studio Code and select the PowerShell Terminal that should be open on the lower end of VSCode. If Terminal is not in sight, you can alternatively open it by expanding the Terminal-dropdown on the top, and selecting New Terminal.

  5. Now, login to Azure by executing:

    az login
  6. Select the right subscription you want to work in by executing the following command. Update the <subscription id> with your Subscription Id. This will start an interactive login session opening your default web browser.

    az account set --subscription "<subscription id>"
  7. Create a new Service Principal with Owner permissions at subscription level by executing the following command:

    az ad sp create-for-rbac --name "<<service-principal-name>>" --role "Owner" --sdk-auth --output "json"

If you don't want to assign Owner, you can also choose Contributor in combination with User Access Administrator. For this second role assignment you will need to execute the following command:

```Powershell
az role assignment create --assignee "<<service-principal-name>>" --role "User Access Administrator"
```
  1. The below output will be returned when the service principal has been created. Make sure you copy these values in a Notepad, for instance.

    {
        "clientId": "<client_id>",
        "clientSecret": "<client_secret>",
        "subscriptionId": "<subscriptionId>",
        "tenantId": "<tenant_id>",
        (...)
    }

    Note: Make sure to write these things down as you will need them in the next Lab. Especially the password since it can't be retrieved twice.

  2. Lastly, you need to gather the Object Id of the Service Principal you just created. You can do so by executing the following command:

    az ad sp list --display-name "<service-principal-name>" --query "[].objectId" --output tsv
Alternative 2: Via the Azure Portal

Create the Service Principal

  1. Open the Azure Portal via the URL https://portal.azure.com

    Portal Home
  2. Navigate to Azure Active Directory (Azure AD) by using for example the search bar on the top.

    Portal Search AAD
  3. Here we want to do 2 things:

    • Make note of your Tenant ID in the displayed Overview for later reference
    • Further select App registrations in the blade to the left
    Portal AAD Home
  4. In the opening view select + New registration on the top.

    Portal AAD App Registration Overview
  5. In the opening form, please provide a name of your choice and select Register. All other options can remain as is.

    Portal AAD App Registration
  6. This will open the created application's overview. Here we again want to do 2 things:

    • Make note of the Application (client) ID for later reference
    • Navigate to the application's underlying service principal by selecting its name on the right side of the application's overview
    Portal AAD App View
  7. Here you have to perform 2 tasks:

    • Make note of the service principal's Object ID for later reference
    • Further, navigate back to the created application's overview by going back in your browser
  8. Back on the application, you again have to perform 2 tasks:

    • Select Certificates & secrets in the blade to the left
    • Select + New client secret in the opening view to the right
    Portal AAD App Secret Create
  9. Now enter a name for the secret, and click on Add.

    Portal AAD App Secret Create Blade
  10. The previous step created a new secret for the application which is shown to us now. Please make note of the secret value as it will only be visible until we leave this view.

    Portal AAD App Secret View

Grant Service principal access to subscription

Now that we have a new service principal, we must grant it access on the subscription we want to test later deployments in.

  1. As a first step, you have to navigate to your subscription by using for example the search bar on the top.

    Subscription search
  2. Following, select the subscription you want to grant access to. In the below example it is called Visual Studio Enterprise Subscription.

    Subscription select
  3. This brings you to the overview of your subscription. Here you need to perform 3 tasks:

    • Make note of the Subscription ID for later reference
    • Make note of the Parent Management Group for later reference
    • Further select Access control (IAM) in the blade to the left
    Subscription overview
  4. In the opened Access control (IAM) view, select + Add on the top.

    Role assignment overview
  5. In the opening drop-down, select Add role assignment.

    Role assignment init
  6. This opens a new view with 3 tabs we have to navigate through. First, you must select the Role we want to assign. For this lab we recommend to use the Owner role, as the CI platform tests both resource deployments, as well as role assignments. If you don't want to assign Owner, you can also choose Contributor in combination with User Access Administrator. In either case, you can use the search to search for the roles you want to assign and select them in the list below it.

    Role assignment select role
  7. Next, navigate to the Members tab and click on + Select members.

    Role assignment member initial
  8. In the opening blade, search for the name of the application you created (in this example it is MyApplication), make sure you select the application, and click on Select on the bottom of the blade.

    Role assignment select member
  9. This will close the blade and show the application in the Members view as shown in the image below.

    Role assignment member selected
  10. Finally, select the Review + assign tab which shows you an overview of the configuration and click on the Review + assign button on the bottom to conclude the role assignment.

    Role assignment Review
  11. This will close the view and we can confirm that the role assignment worked by searching for the application in the search and selecting your application in the Check access section. Be aware that it may take a few seconds for the role assignment to finish.

    Role assignment check access search
  12. In the opening blade you can see that the application indeed is configured with the expected role(s) for the current subscription.

    Role assignment check access

Step 2 - Create your fork

In CARML, you can't work directly in the main branch, so the first action to be taken is to fork the repository.

One of the fundamental features in version control systems, branching and forking are fundamentals to collaboration.

A branch in Git creates another line of development in the project without affecting the main branch.

Fork, on the other hand, is a clone of the repository on a different organization and you can do it even if you don't have contribution rights on the main repo.

  1. Open to the repository via the URL aka.ms/CARML

    Repository main
  2. Select the Fork button on the top right and select the organization you'd like to fork into. For this lab we recommend to use your own organization as this ensures you will have all the permissions you need.

    Repository Fork
  3. Once triggered, the repository will take a moment to be set up.

    Forking in progess
  4. Once done, you will automatically be forwarded to your repository.

    Forked repository

Step 3 - Configure your repository

Now you need to configure several secrets that are leveraged by the solution's workflows. Most notably for example, the service connection.

To do that you have to perform the following steps in sequence:

  1. Navigate to the repository's Settings.

    Navigate to settings
  2. In the list of settings, expand Secrets and select Actions. You can create a new repository secret by selecting New repository secret on the top right.

    Navigate to secrets
  3. In the opening view, you can create a secret by providing a secret Name, a secret Value, followed by a click on the Add secret button.

    Add secret

    The values of the secrets are the ones we collected in Step 1 of this lab. You'll need to create one secret for each entry in the following table and make sure you use the exact same naming:

    Secret Name Example Description
    ARM_MGMTGROUP_ID de33a0e7-64d9-4a94-8fe9-b018cedf1e05 / my-mg The group ID of the management group to test deploy modules of that level in.
    ARM_SUBSCRIPTION_ID d0312b25-9160-4550-914f-8738d9b5caf5 The subscription ID of the subscription to test deploy modules of that level in.
    ARM_TENANT_ID 9734cec9-4384-445b-bbb6-767e7be6e5ec The tenant ID of the tenant to test deploy modules of that level in.
    AZURE_CREDENTIALS See below See below
    PLATFORM_REPO_UPDATE_PAT <placeholder> A private access token (PAT) with enough permissions assigned to it to push into the main branch. This PAT is leveraged by pipelines that automatically generate ReadMe files to keep them up to date.
    TOKEN_NAMEPREFIX cntso Optional. If you specify the name prefix token here, it is only applied if the localToken_namePrefix specified in the settings.yml is left empty.
    • Special case: AZURE_CREDENTIALS, This secret represents our service connection to Azure and its value is a compressed JSON object that must match the following format:

      {"clientId": "<client_id>", "clientSecret": "<client_secret>", "subscriptionId": "<subscriptionId>", "tenantId": "<tenant_id>" }

      Make sure you create this object as one continuous string as shown above - using the information you collected during Step 1 of this lab. Failing to format the secret as above, results in masked strings (***) in place of { and } in the workflow logs as it is considering each line of the json object as a separate secret string. If you're interested, you can find more information about this object here.

Step 4 - Configure code base

Clone the repository

To perform these changes as quickly and easily as possible, we recommend to update the code base using a local clone of the code in Visual Studio Code (VSCode). To do so, please perform the following steps:

  1. On the overview page of your fork, select the <> Code button to the right, and select the copy button in the opening pop up to copy the URL we need to clone the repository.

    Clone fork
  2. On your local machine you can clone the repository to any location you desire. However, it is recommended to keep the local file paths short.

    Example: C:/dev/CARML/lab

    Local folder

    There are different ways you can achieve this:

    Alternative 1: Using VSCode
    1. Open Visual Studio Code and further navigate to the Terminal. If a Terminal is not in sight, you can alternatively open it by expanding the Terminal-dropdown on the top, and selecting New Terminal

    2. In the Terminal, type cd <Path>, and replace <Path> with the local file path you'd like to clone your fork to (for example cd 'C:/dev/CARML/lab')

    3. Next, type git clone '<URL>', replace <URL> with the URL you copied before and confirm the execution.

    4. Following the execution you can now open the downloaded clone via the file explorer on the top left

      Open file explorer
    5. Navigate to the extracted folder, select it and trigger the Select Folder button

      Select folder
    Alternative 2: Using the Terminal
    1. In the folder you chose, perform a right-click and select Open in Windows Terminal to open a new PowerShell session with the path set to the current folder.

      Open local terminal
    2. In the Terminal type git clone '<URL>', replace <URL> with the URL you copied before and confirm the execution.

      Clone in terminal
    3. Following the execution you will find that there is now a ResourceModules folder.

      Local Folder
    4. Next, back in the Terminal, execute the command code ./ResourceModules to open Visual Studio Code in the clone repository folder.

      CARML in VSCode

Update default namePrefix

To lower the barrier to entry and allow users to easily define their own naming conventions, we introduced a default "name prefix" for all deployed resources.

Each pipeline in CARML that deploys resources uses a logic that automatically replaces "tokens" (i.e. placeholders) in any module test file. Tokens are stored in only a few central locations to facilitate maintenance (e.g., local settings.yml, repository secrets or variable groups)..

To update the namePrefix, perform the following steps:

  1. Open the settings.yml file in the repository's root directory.

  2. Replace the value of the localToken_namePrefix with a different value:

    localToken_namePrefix: 'cntso'

    NOTE: The value should be a 3-5 character long string like cntso. Longer strings are not recommended as they may conflict with Azure resource name length restrictions.

    NOTE: The CI pipelines automatically removes the localToken_ prefix from the name when processing the tokens replacement.

Commit your changes to main

You now need to push the changes in the repo. You can do this in two ways:

Alternative 1: Via VSCode's terminal
  1. If a Terminal is not in sight, you can alternatively open it by expanding the Terminal-dropdown on the top, and selecting New Terminal

  2. Now, execute the following PowerShell commands:

    git add .
    git commit -m 'Update settings'
    git push
Alternative 2: Via VSCode's UI
  1. Add your changes: If not already there, navigate to the source control menu to the left and add the changed files to the commit. To do so, select the + icon next to the settings.yml file

    Open source control
  2. Commit your changes: Next, you should give the commit a meaningful message such as 'Update settings' and can then click the checkmark symbol on the top to create the commit

    Git commit
  3. Push your changes: Finally, you can push the changes to the repository by selecting the blue Sync Changes button

    Git push

Forgot to configure git?

If you get an issue regarding configuring git user name and email, run the following commands with your info:

git config --global user.name "John Doe"
git config --global user.email "johndoe@example.com"

Step 5 - Create a branch

By default, CARML uses pipeline triggers to automate for example the publishing of a new module once a corresponding PR is merged.

To this end, the trigger is set up to look for any changes in the main branch if any module or pipeline file was modified.

As you don't want to accidentally trigger any pipelines, you should hence create a branch to perform your tasks on throughout the rest of the lab.

You can do this in one of two ways:

Alternative 1: Via VSCode's terminal
  1. If a Terminal is not in sight, you can alternatively open it by expanding the Terminal-dropdown on the top, and selecting New Terminal

  2. Now, execute the following PowerShell commands:

    git checkout -b 'carmlLab'
    git push --set-upstream 'origin' 'carmlLab'
Alternative 2: Via VSCode's UI
  1. Select the current branch on the bottom left of VSCode

    Change branch main
  2. Select + Create new branch in the opening dropdown

    Init create branch
  3. Enter the new branch name carmlLab

    Enter name
  4. Push the new branch to your GitHub fork by selecting Publish Branch to the left in the 'Source Control' tab

    Git push

Step 6 - Enable actions

Finally, the 'GitHub Actions' are disabled by default. Hence, in order to continue with the rest of the lab and execute any pipelines you have to enable them first.

To do so, perform the following steps:

  1. Navigate to the Actions tab on the top of the repository page.

    Navigate to actions
  2. Next, select 'I understand my workflows, go ahead and enable them'.

    Enable Actions

Appendix

Check name-prefix availability snippet

To check the availability of the name-prefix please copy and paste the below snippet into a PowerShell window and provide a name for the requested namePrefix parameter.

function Test-NamePrefixAvailability {

    param(
        [Parameter(Mandatory = $true)]
        [string] $namePrefix
    )

    if(-not (Get-AzContext)) {
      Write-Warning "In order to execute this function you must be logged into Azure. Initiating..."
      Connect-AzAccount
    }

    $prefixAvailable = $true

    # Build names
    $storageAccountNames = @(
      ('adp{0}azsafa001' -f $namePrefix),
      ('{0}azsax001' -f $namePrefix)
    )
    $keyVaultNames = @(
      ('adp-{0}-az-kv-x-001' -f $namePrefix),
      ('{0}-az-kvlt-x-001' -f $namePrefix)
    )
    $acrNames = @(
      ('adp{0}azacrx001' -f $namePrefix)
    )

    # $accessToken = Get-AzAccessToken

    $subscriptionId = (Get-AzContext).Subscription.Id

    # Storage
    Write-Host "`nCheck Storage Accounts" -ForegroundColor 'Cyan'
    Write-Host '======================' -ForegroundColor 'Cyan'
    foreach ($storageAccountName in $storageAccountNames) {
        $path = '/subscriptions/{0}/providers/Microsoft.Storage/checkNameAvailability?api-version=2021-04-01' -f $subscriptionId
        $requestInputObject = @{
            Method  = 'POST'
            Path    = $path
            Payload = @{
                name = $storageAccountName
                type = 'Microsoft.Storage/storageAccounts'
            } | ConvertTo-Json
        }
        $response = ((Invoke-AzRestMethod @requestInputObject).Content | ConvertFrom-Json)
        if ($response.nameAvailable) { Write-Host "=> The storage account name [$storageAccountName] is currently available" -ForegroundColor 'Green' }
        else {
            Write-Warning "=> Warning: The storage account name [$storageAccountName] is not available. Please try a different prefix."
            $prefixAvailable = $false
        }
    }

    # Key Vault
    Write-Host "`nChecking Key Vaults" -ForegroundColor 'Cyan'
    Write-Host '===================' -ForegroundColor 'Cyan'
    foreach ($keyVaultName in $keyVaultNames) {
        $path = '/subscriptions/{0}/providers/Microsoft.KeyVault/checkNameAvailability?api-version=2021-10-01' -f $subscriptionId
        $requestInputObject = @{
            Method  = 'POST'
            Path    = $path
            Payload = @{
                name = $keyVaultName
                type = 'Microsoft.KeyVault/vaults'
            } | ConvertTo-Json
        }
        $response = ((Invoke-AzRestMethod @requestInputObject).Content | ConvertFrom-Json)
        if ($response.nameAvailable) { Write-Host "=> The key vault name [$keyVaultName] is currently available" -ForegroundColor 'Green' }
        else {
            Write-Warning "=> Warning: The key vault name [$keyVaultName] is not available. Please try a different prefix."
            $prefixAvailable = $false
        }
    }

    # Azure Container Registry
    Write-Host "`nCheck Azure Container Registies" -ForegroundColor 'Cyan'
    Write-Host '==============================' -ForegroundColor 'Cyan'
    foreach ($acrName in $acrNames) {
        $path = '/subscriptions/{0}/providers/Microsoft.ContainerRegistry/checkNameAvailability?api-version=2019-05-01' -f $subscriptionId
        $requestInputObject = @{
            Method  = 'POST'
            Path    = $path
            Payload = @{
                name = $acrName
                type = 'Microsoft.ContainerRegistry/registries'
            } | ConvertTo-Json
        }
        $response = ((Invoke-AzRestMethod @requestInputObject).Content | ConvertFrom-Json)
        if ($response.nameAvailable) { Write-Host "=> The Azure container registry name [$acrName] is currently available" -ForegroundColor 'Green' }
        else {
            Write-Warning "=> Warning: The Azure container registry name [$acrName] is not available. Please try a different prefix."
            $prefixAvailable = $false
        }
    }

    Write-Host "`nRESULT" -ForegroundColor 'Cyan'
    Write-Host '======' -ForegroundColor 'Cyan'
    if (-not $prefixAvailable) {
        Write-Error "=> Prefix [$namePrefix] is not available for all resources. Please try a different one."
    }
    else {
        Write-Host "=> Prefix [$namePrefix] is available for all resources." -ForegroundColor 'Green'
    }
}
Test-NamePrefixAvailability


If ready, proceed to the next lab: Lab 3 - Deploy dependencies