Saturday, July 22, 2017

Static Code Analysis for ARM Templates?

In a previous post I discussed the fact that ARM templates have an interesting flow when it comes to dealing with passwords.  The recommendation was that the ARM templates should be reviewed on each check-in.  This got me thinking that maybe we need a static code analysis tool for ARM templates.  The goal of this post is to discuss this in more detail.

For a long time now, code analysis tools (particularly static ones) have been in use for many managed and un-managed languages.  It is so popular that code analysis is built-in to Visual Studio 2017.  In fact, security guidance generally recommends the use of these tools.  Taking this further, many development shops use this type of analysis for not only enforcing code quality but also enforcing style guides, etc.  As ARM templates are "infrastructure-as-code" it only makes sense to try and extend these processes to those artifacts as well.

I think there are two avenues that static analysis could assist with ARM templates.  Firstly, it could identify common security concerns that may be found in these templates.  The first one that comes to mind is how passwords are handled.  As my previous post discussed, you can pass in passwords via securestring, or you can use keyvault references.  The better method is to use keyvault, and so we could write a test that ensures no parameters of type "securestring" exists.  This is just one example.

The second use for this would be around style.  One example is the use of description attributes on parameter elements.  You could issue a warning (for example) if a parameter was missing this attribute.  This could help enforcing good design guides at scale.

I know that Microsoft has been working on expanding Azure Resource Policies, and while a step in the right direction, they focus more on what can be created  in Azure (and what properties can/should/must be set) than how a template is written.  I do see a need where rules are potentially enforced in Azure (via ARP) and also, potentially, enforced in a build-step.  The build-step aspect would provide rapid feedback to the developers of policy violations (usability, security, or otherwise).

Ultimately, coming up with a full set of tests is outside the scope of this blog post.  I'm really looking for some feedback from the community on the viability of something like this.  For completeness, here is some example code that could be used to write tests.


#
# Script.ps1
#

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

class ARMParameter{
 [string]$name
 [string]$type
 [string]$metadata
 ARMParameter([string]$name,[string]$type,[string]$metadata){
  $this.name = $name
  $this.type = $type
  $this.metadata = $metadata
 }
 [string] ToString(){
  return "{0}-{1}" -f $this.name,$this.type
 }
}

class ARMResource{
}

Class ARMTemplate{
 hidden [PSCustomObject]$json
 hidden [ARMParameter[]]$parameters

 hidden [Void] ParseParameters(){
  Write-Verbose "Entering ParseParameters"
  $parsedParameters = @()
  $parametersRaw = $this.json.parameters
  foreach ($parameterRaw in $parametersRaw.psobject.Properties){
   Write-Verbose "parsing $parameterRaw"
   $name = $parameterRaw.Name
   $type = ""
   $metadata = ""
   foreach ($parameterRawContent in $parameterRaw.value.psobject.Properties){
    if ($parameterRawContent.Name -eq "type"){
     $type = $parameterRawContent.value
    }

    if ($parameterRawContent.Name -eq "metadata"){
     $metadata = $parameterRawContent.value.description
    }
   }
   $parsedParameters += [ARMParameter]::new($name,$type,$metadata)
  }
  $this.parameters = $parsedParameters
 }

 ARMTemplate([string]$filePath){
  Write-Verbose "Creating ARMTemplate Object"
  $this.json = ConvertFrom-Json -InputObject ((Get-Content -Path $filePath) -Join "`n")
  $this.ParseParameters()
 }

 [PsCustomObject[]] GetParametersByType([string]$type){
  return $this.parameters | ? {$_.type -eq $type}
 }

 [PsCustomObject[]] GetParameters(){
  return $this.parameters
 }
}


if (!(Test-Path $filePath)){
 Write-Error ("$filePath does not exist")
 exit
}
$armTemplate = [ARMTemplate]::new($filePath)

Write-Host "Test 1: Parameters with securestring" -ForegroundColor Cyan
$parameterWithTypeSecureString = $armTemplate.GetParametersByType("securestring")
if ($parameterWithTypeSecureString.length -eq 0){
 Write-Host "PASSED: No parameters with securestring type found" -ForegroundColor Green
} else {
 Write-Host "FAILED: Review Required: Paramters found with securestring type" -ForegroundColor Red
 Write-Host $parameterWithTypeSecureString
@"
Notes
===========
 Since these securestring parameters are passed in via the template, a reviewer should ensure that the pipeline for deployment
 handles the password in a secure manner.  An example would be a build server that retrieves the password from a secret store.
 Pipelines that give the developer access to the password for deployment purposes, or that store the credentials in plain-text
 should be avoided.
"@  
 
}


Write-Host "Test 2: All parameters should have metadata flag" -ForegroundColor Cyan
$parameters = $armTemplate.GetParameters()
$parametersWithNoMetadata = $parameters.Where({[string]::IsNullOrEmpty($_.metadata)})
if ($parametersWithNoMetadata.length -gt 0){
 Write-Host "Failed: Review Required: Metadata description missing from parameter fields" -ForegroundColor Red
 Write-Host "The following properties should have a metadata description added"
 Write-Host $parametersWithNoMetadata
@"
Notes
===========
 All parameters should contain a metadata tag with a description tag that defines the purpose of the property and where it 
 is used in the template.
"@  
} else {
 Write-Host "Passed" -ForegroundColor Green
}


So what are some thoughts around this concept?  Is is worth creating something like this where a team could create "standards" both from a usability perspective and a security perspective, and have it enforced (likely as part of a build pipeline)?  Does one of these exist in the marketplace and I am simply unaware?




Wednesday, July 19, 2017

Enterprise Cloud Strategy Part 2 - Experimentation

So far in our tour of Microsoft's Enterprise Cloud Strategy Book, we've discussed the 5 R's as a methodology for deciding how to "modernize" your applications.  The next section of this book discusses three common steps to a cloud migration and then focuses on the concept of experimentation.

What is experimentation in this context?

In the context of the book, the authors describe experimentation as a key step to cloud adoption.  In this step, "the engineers and others create the IT department's first cloud applications, with the objective of learning what the cloud is all about...".  The goal, of course, is to give IT the opportunity to learn about the cloud and the various aspects of building applications that live in the cloud.  One interesting aspect is how the book defines the principles of "a culture of experimentation".


My Thoughts

In my opinion, safe to fail experiments are key to the success of any projects in IT.  While I'd like to think that cloud computing does not meet the traditional definition of a complex system, the act of designing solutions in the cloud can be extremely difficult.  Moving business targets, ever-changing cloud capability landscape, and endless possible effective solutions decrease the predictability of design outcomes for similar projects.  Safe to fail experiments are key to testing out architectures that push the boundaries of the cloud platform, make use of the latest enhancements/improvements, and provide targeted feedback for lessons learned.

The second point I'd like to make is that the book makes mention of experimentation as a first step to an organization adopting the cloud.  I would argue that this is an important step not only in the initial phases of an overall roadmap, but also important for every single project that is in scope.  When I am working with my clients, I always push for a proof of concept phase as part of a cloud migration. What this allows us to do is:

  • Put an appropriate amount of design work in, without having to define everything up front
  • Experiment/test with multiple architectures to test for best fit
  • Put focus on delivering repeatable steps via automation
This type of approach has many benefits in my mind.  Firstly, proof of concept phases can be treated as safe-to-fail experiments, allowing the delivery teams flexibility in approach and output.  Proof of concept phases are short in duration, which allow for quick iteration on target architectures.  The focus on automation not only increases the velocity of future work, it supports a more agile delivery approach.  From a business perspective, proof of concept phases help drive down uncertainty/risk in the rest of the project delivery, while increasing the accuracy of estimates around time and effort for project completion.

In conclusion, I'm a big believer of experimentation as an important aspect of project delivery, big or small.  In the context of the book, experimentation is a phase where IT is given the chance to learn more about the various aspects of the cloud and how to run applications within it.  It fits well with a "crawl,walk,run" approach, and one that can pay dividends in the long run.  My addition would be that experimentation is not only fit for the start of a cloud journey, but also fit for every project within that program.



Saturday, July 15, 2017

Enterprise Cloud Strategy Part 1 - The 5 R's

Microsoft has released the 2nd edition of it's Enterprise Cloud Strategy book, and it has a ton of good content for companies looking to make the jump to cloud.  The beauty of this type of book is that it really is "cloud agnostic".  While specific examples and references are made to the Azure platform, you can use the concepts in here to plan your cloud strategy regardless of the target cloud.

I wanted to do a book review on this book, but I quickly realized that it would be a large undertaking.  There is so many core concepts discussed in this book and it would be hard to capture all of it in one post.  I've decided to write a short series on different aspects covered and my thoughts/experiences in dealing with clients in these specific areas.

Part 1, this post, will chat about the 5 R's of modernization.

When enterprises are making the move to the cloud, there is generally a business reason to do so.  The business derives values from the applications that IT hosts/maintains/builds for them, and therefore, a lot of cloud migration discussion focus on how to get applications "to the cloud".  We are not all fortunate to work with green-field cloud apps, and the 5 R's represent a set of actions one could take to migrate their apps.

The first one is retire.  This is an often overlooked option when reviewing applications for a cloud migration.  Simply put, there is always an option to mark an application for retirement and not consider it for a cloud migration.  Start working with the business to decommission the application and determine a best fit for areas of capability that are still required.

The second option is to replace.  From experience, I find that very few companies are interested in the replace option as a first step, but it is important to consider as part of the process.  With the pace of technological innovation, especially in the SaaS space, there is a good chance that the legacy application in scope might have an acceptable alternative in the marketplace.  I always suggest a short period where business SMEs and a cloud architect investigate options in the field.  One important note is that this is generally only viable when the application is non-differentiating.  Applications that act as the 'secret sauce' that separate your client from it's competition are generally not good for a pre-canned SaaS app.  Another con to this approach is integration.  Chaining SaaS applications together to deliver business value can be a difficult task.

The third option is to retain, wrap, and expand.  When considering this option, one must understand why we are moving the particular application to the cloud.  Retain/Wrap strategies are good when changes cannot be made to the application.  In my experience, I have rarely seen legacy applications written to be able to accommodate such a pattern.  Data replication patterns seem to be a best fit here, allowing you to isolate the application (and then move it) while still making the data accessible to other downstream systems.  Generally this method is used to save costs, so one must understand the ingress/egress and storage costs associated.

Expanding an application is a interesting one.  One use case I have seen is the idea of batched operation, particular for simulations.  Cloud services can be used to provide on-demand capacity to existing processes.  Many Azure batch functions work with existing HPC techniques and toolsets.

The next option is to rehost.  I always think of this as one of the "boring" approaches to cloud migrations.  In most cases, you can mimic your existing environment in the cloud, and simply move the hosting of the application there.  One thing that is often overlooked in this approach is the cost of integration with downstream systems, security systems, etc.  I've rarely found an isolated application (of value) that did not involve some degree of conversation around integration when this approach is used.  Further, integration methods that work great on-premises could have large latency/bandwidth considerations in the cloud.

The last option is reenvision.  Many companies without large development teams in-house tend to shy away from this approach.  Others have their development teams engaged in new features and not available to completely redesign the application.  While this approach may seem daunting, it often can be justified from a cost perspective.  The development time spent not only allows a legacy system to better meet the current business requirements, a modern architecture can help the application better scale/integrate/etc, allowing for easier enhancements into the future. 

In conclusion, there are pros/cons to every approach from a cloud migration perspective.  One must really understand the main business drivers for the overall cloud strategy as well as the particular application before choosing a suitable method.  Cost/Time/Resource concerns all play in to picking a successful migration strategy.