Day 70: A Read-Only User and a Template

Day 70: A Read-Only User and a Template

# devops# jenkins# azure# cicd
Day 70: A Read-Only User and a TemplateNnamdi Felix Ibe

Day 70 of DevOps, Day 20 of Azure. Both tasks today were about what a system does to the things you...

Day 70 of DevOps, Day 20 of Azure. Both tasks today were about what a system does to the things you did not mention. In Jenkins, a permission you do not tick is denied, which is the easy half. In ARM, a property you leave out of a template is reset to its default on the next deployment, which is not what the word incremental led me to expect.

One Jenkins task, one Azure task. Create a user and give them read-only access, then edit and deploy an ARM template. The tasks come from the KodeKloud Engineer platform.

Task 1: A read-only Jenkins user

The task: create kirsty with full name Kirsty and password BruCStnMT5, then grant read-only access using a project-based matrix authorisation strategy.

Step 1: Create the user

Manage Jenkins, Users, Create User, with the three values the task gave. That part is a form.

Step 2: Install the strategy, because it is not in Jenkins

This is the step the task's wording assumes you will work out. Matrix Authorisation Strategy is a plugin, not core, described on its own page as offering "matrix-based security authorisation strategies (global and per-project)". On a Jenkins where nobody has installed it, the Security page has no matrix option at all, so the authorisation half of the task cannot start until it is installed.

Jenkins names the two strategies the plugin adds as "Matrix-based security" and "Project-based Matrix Authorisation Strategy", listing them alongside the core choices "Anyone can do anything", "Legacy mode" and "Logged in users can do anything". The plugin's own page calls the same things global and project-based matrix authorisation. Two vocabularies, one feature.

The difference is whether per-item permissions exist at all. Matrix-based security is a single global grid. Project-based adds a second grid on every job and agent, which is what the requirement means by project-based.

Step 3: Grant Overall/Read globally

Manage Jenkins, Security, switch the strategy, add kirsty, tick Overall/Read.

This one goes first for a documented reason, with a caveat about how hard it is to state. The plugin says, "A user not granted read access to Jenkins in general will not be able to use most of the other permissions." Jenkins' permissions reference is more careful: "While Overall/Read is a prerequisite for more extensive access to Jenkins, some features are available without it."

So "nothing works without Overall/Read" is too strong, and "most of it does not" is right.

Step 4: Grant Read on the job

Open the job, Configure, enable project-based security, add kirsty, tick Read.

Item-level grants add to the global ones, and that is a default rather than a property of the strategy. The plugin: "Permissions explicitly granted on individual items or agents will only add to permissions defined globally." Two other inheritance modes exist, "Inherit global configuration only" and "Do not inherit permissions", and under the last of those nothing is additive.

Step 5: Log in as the user, because the grid tells you nothing

A matrix of ticks looks correct whether or not it is. The only check worth doing is logging out and back in as kirsty.

The dashboard comes up with no Manage Jenkins entry. The job opens and offers nothing beyond reading it. That is the requirement expressed as behaviour rather than as configuration, and it is the difference between having set the permissions and having set the right ones.

What the docs recommend

Know the way out before you go in. Jenkins documents the lockout case plainly: administrators "may accidentally set up a security realm or authorisation strategy" such that they can no longer administer Jenkins. The recovery is to stop Jenkins, edit config.xml in the Jenkins home directory, "Look for the elements <securityRealm> and <authorizationStrategy> and remove them", set <useSecurity> to false, and restart.

With a warning worth reading twice: "Jenkins will be in an entirely unsecured mode after it starts, allowing anyone full access." So the recovery is a window during which the controller is open to everyone, which is an argument for having a second administrator account rather than a recovery procedure.

Put the matrix in a file. This is Day 69's Configuration as Code point arriving with a sharper reason. A security grid built by clicking has no diff and no history, so the state you locked yourself out of is also the state nobody can describe. In a configuration file, it is reviewable, and for a Configuration as Code installation, the recovery is editing the authorizationStrategy directive rather than deleting elements from config.xml.

Task 2: Edit and deploy an ARM template

The task: change a template's VNet name, address space and tags, then deploy it with the CLI. The first infrastructure-as-code task on the Azure track, after CloudFormation on Days 47 and 48.

Step 1: Edit by path, not by pattern

cp vnet-deployment-template.json vnet-deployment-template.json.bak
jq '.resources[0].name = "arm-vnet-devops"
  | .resources[0].tags.displayName = "arm-vnet-devops"
  | .resources[0].tags.Environment = "KKE-devops"
  | .resources[0].properties.addressSpace.addressPrefixes = ["192.168.0.0/16"]' \
  vnet-deployment-template.json.bak > vnet-deployment-template.json
Enter fullscreen mode Exit fullscreen mode

sed would have worked on this file and breaks the moment whitespace, key ordering or a repeated string differs. "virtualNetwork1" appears twice here, as the name and as the displayName, and both needed the same new value, so a blind substitution would have been right by luck rather than by intent.

Two details in that command matter. jq file > file empties the file, because the shell truncates a redirect target before the command runs: "If the file does not exist it is created; if it does exist it is truncated to zero size." Reading from the backup gives the read and the write separate paths. And assigning tags.Environment creates the key while leaving displayName alone, whereas assigning .tags = {...} would have replaced both.

Step 2: Validate, then preview

az deployment group validate -g $RG --template-file vnet-deployment-template.json
Enter fullscreen mode Exit fullscreen mode
{ "Error": null, "Prov": "Succeeded" }
Enter fullscreen mode Exit fullscreen mode

This is more than a syntax check. The command takes a --validation-level, and "With a validation level of 'provider', ARM will perform full validation and check that you have sufficient permission to deploy all resources in the template". Only the template level skips "preflight and permissions checks".

Which is a real difference from the AWS equivalent. On Day 47, aws cloudformation validate-template returned clean, and the stack then rolled back on an IAM permission. The limitation is documented, though not where you would look first: the API reference does not mention it, and the User Guide says, "The validate-template command is designed to check only the syntax of your template. It does not ensure that the property values that you have specified for a resource are valid for that resource."

There is a better pre-flight check still:

az deployment group what-if -g $RG --template-file vnet-deployment-template.json
Enter fullscreen mode Exit fullscreen mode

"The what-if operation doesn't make any changes to existing resources. Instead, it predicts the changes if the specified template is deployed." So validate answers whether this will run, and what-if answers what it will do.

Step 3: Deploy

az deployment group create -g $RG -n arm-vnet-deploy --template-file vnet-deployment-template.json
Enter fullscreen mode Exit fullscreen mode
"Mode": "Incremental", "provisioningState": "Succeeded"
Enter fullscreen mode Exit fullscreen mode

The word incremental does not mean what I assumed

I had been carrying a comfortable version of this: Incremental mode leaves unrelated resources alone, and how much of an existing resource's state survives a redeploy varies by type.

Microsoft aims the warning directly at that assumption. "A common misunderstanding is to think properties that aren't specified in the template are left unchanged." What happens instead: "When redeploying an existing resource in incremental mode, all properties are reapplied. The properties aren't incrementally added", and "Properties that aren't included in the template are reset to the default values."

So Incremental is incremental about resources, not properties. A resource the template never mentions is left alone. A resource the template does mention is rewritten to the template's definition, and everything the template omits goes back to its default.

The type-specific caveat is real, and it is a short list of exceptions rather than a general hedge: "Two common examples are subnets on virtual networks and site configuration values for web apps." For subnets, "As long as the subnets are defined on the virtual network, you can redeploy the virtual network and not lose the subnets."

Which turns a vague rule of thumb into a concrete one. Add a tag to this VNet later with az tag update, as on Day 12, then redeploy this template unchanged, and the tag goes. Not maybe. Run what-if first, and it will tell you.

What the docs recommend

Keep Complete mode away from a shared resource group, and know it is on its way out. Complete mode is where "Resource Manager deletes resources that exist in the resource group but aren't specified in the template", so this single-VNet template would empty a populated group. Microsoft now steers away from it entirely: "Use deployment stacks to perform resource deletions when using ARM templates or Bicep files, as the complete mode will be gradually deprecated."

Do not expect a deployment to own anything. This is the core difference from CloudFormation and the reason Day 48's one-command teardown has no equivalent here:

CloudFormation stack ARM deployment
Deleting it "CloudFormation deletes all resources in that stack" "Deleting a template deployment does not affect the state of the resource group."
On a failed create OnFailure defaults to ROLLBACK No automatic undo
Teardown delete-stack Delete the resources, or the resource group

Deployment Stacks are the newer answer and do add ownership: a stack "enables you to manage a group of Azure resources as a single, cohesive unit", and a resource removed from the template "can be detached or deleted based on the specified actionOnUnmanage behaviour". The default is still cautious, since stacks "detach and don't delete unmanaged resources".

Parameterise it. The template's parameters and variables are both empty, with the name, address space and tags hardcoded, which is why this task was a text edit. It is also why the file is single-use. Three parameters and "name": "[parameters('vnetName')]" turn the editing exercise into:

az deployment group create -g $RG --template-file ... --parameters vnetName=arm-vnet-devops
Enter fullscreen mode Exit fullscreen mode

Keep the two good habits the template already has. "location": "[resourceGroup().location]" makes region inheritance explicit rather than an unstated CLI default, which is why this VNet landed in eastus without anyone naming a region. And "apiVersion": "2023-11-01" pins the provider API, which the best-practice guidance wants hard-coded: "Set the apiVersion property to a hard-coded API version for the resource type", and specifically "Don't use a parameter for the API version."

Finally, watch the history limit, because it fails rather than grooms. A scope "can store up to 800 deployments in its history", grooming starts above 700 and trims to 600, and "If your scope is already at the 800 limit, your next deployment fails with an error." The grooming runs as the deploying user and needs delete permission, and a CanNotDelete lock stops it. So a locked resource group with a busy pipeline can hit 800 and start failing deployments for a reason that has nothing to do with the deployment. Note that what-if and validate runs count too.

What I learned

Jenkins is explicit about absence. If you don't grant a permission, it's denied, the grid shows every box you didn't tick, and the only surprise is that the strategy itself arrives as a plugin.

ARM is the opposite, and the mode's name is part of the problem. Incremental sounds like it adds to what is there. For a resource the template names, it replaces, and the properties you left out go back to defaults. My notes had a hedged version of this, and the hedge was doing the work of a warning.

So here is the Day 70 question. In your infrastructure code, what is currently true about a resource that the code itself does not say?

Day 70 down. Thirty to go.