Writing Kubectl plugin with PowerShell
Tags: powershell, azure, kubernetes, devops
Published: 2020-03-15 17:34:21

The more and more I deploy workload on Kubernetes using Azure Kubernetes Service. More I use also [Windows Containers]( https://dev.to/omiossec/windows-containers-in-azure-kubernetes-services-1i0c). 

If you operate Kubectl, you can be sometimes frustrated as you need to type diverse commands to do some tasks or use complex expressions with -jsonpath to extract the information you need. What if you were able to automate this a little? 
With the 1.12 of Kubernetes, Kubectl supports the addition of subcommands to automate tasks; Kubectl plugin. 

Kubectl plugins are executable files identifiable by a prefix, "kubectl-". The file needs to be located in your path (/usr/bin/path for example on Linux or any directory present in the PATH environment variable on Windows).

Creating a Kubectl plugin is simple as no library or component is needed. Every programming language can be utilized. The only condition is the "kubeclt-" prefix.
When you call the plugin from kubectl you just have to omit the prefix of the plugin file name. For example, if the file name is kubectl-mycmd the plugin name will be mycmd and you can call it by using :

```
kubectl mycmd
```

A plugin can work with arguments. You can pass arguments to the plugin after the plugin name.  

```
kubectl mycmd Arg1 Arg2 ... ArgN
```

As you understand the dash in the file name is transformed into space, so kubectl-my-cmd is translated in kubectl my cmd (But you should avoid that as it will confuse users if your plugin support arguments). If you want to add dash into the plugin name (not the file name) you need to use an underscore, the kubeclt-my_cmd file is translated into kubectl my-cmd. 

**On Linux, how can you use PowerShell to create kubectl plugin?**

If you use PowerShell for a long time, by that I mean you started using PowerShell on Windows, you may think you cannot make a PowerShell script executable. It’s by design. But on Linux, things are different. Execution is not inherited by the file extension, but by file permissions called modes, where x represent execution. So, changing the access attribute is enough to permit the execution of a file.  

```
sudo chmod +x ./kubectl-mycmd
```

But how to tell we want the script to be executed by PowerShell. On Windows, you can use pwsh.exe -f or powershell.exe -f. But on the Linux platform, there is another option you can use the #! Instruction to indicate which script processor to use with the file.

Let’s build a simple plugin. Create a file named kubectl-psversion

```powershell
#! /usr/bin/pwsh

write-host "Version $($psversiontable.PSVersion)"
```
Change the execution attribute 

```
sudo chmod +x ./kubectl-psversion
```
and move the file to /usr/local/bin

```
sudo mv ./kubectl-psversion /usr/local/bin
```

And now you can use the plugin

```
Kubectl psversion 
```
Displaying the PowerShell version currently installed on your workstation is not useful for your daily work with Kubernetes.

I asked myself what are the most time-consuming tasks you have to deal? For example, getting Pods information in a simple table. I often need to collect the state, the name, the node and the restart count for all pods in a namespace. How to do it?
First, the standard output of kubectl can be difficult to parse. Hopefully, there is two other way to get this information Yaml and Json. Powershell handles Json very well. It transforms a Json document into a PsCustomObject object you can deal with.  
To collect the information, you need about pods you can use get pods with the -o parameter

```powershell
$Podslist = (kubectl get pods -o json) | convertfrom-Json
```
But how can we deal with namespaces? Namespace name must be an optional argument for the plugin. Kubectl does not support named parameters, only positional parameters (arg1 arg2 arg3…). The PowerShell script must handle it. Usually, for functions and modules, it’s not a best practice, but there is no other choice. 

```powershell
param(
    [parameter(Position=0)]
    [string]
    $namespace
)
```

If no namespace is passed to the plugin the default namespace should be used. 

```powershell
    if (!($PSBoundParameters.ContainsKey('namespace'))){
        $namespace ="default"
    }
```
Putting all thing together, create a file called kubectl-podsinfops (not kubectl-podsinfops.ps1)

```powershell
#! /usr/bin/pwsh

param(
    [parameter(Position=0)]
    [string]
    $namespace
)

try {
    if (!($PSBoundParameters.ContainsKey('namespace'))){
        $namespace ="default"
    }
    $Podslist = (kubectl get pods --namespace $namespace -o json) | convertfrom-Json
    $PodsArrayList = [System.Collections.ArrayList]::new()
    foreach ($pod in $Podslist.items) {
         [void]$PodsArrayList.Add([pscustomobject]@{ 
            PodName                 =   $pod.metadata.name
            Status                  =   $pod.status.phase
            restartCount            =   $pod.status.containerStatuses[0].restartCount
            StartTime               =   $pod.status.startTime
            image                   =   $pod.status.containerStatuses[0].image
            Node                    =   $pod.spec.nodeName 
            NodeType                =   $pod.spec.nodeSelector
        })
    }
    $PodsArrayList  | Format-Table -AutoSize
}
catch {
     "An error occurred that could not be resolved."
}
```

Make the file executable

```bash
sudo chmod +x ./kubectl-podsinfops
```

move it to your PATH
```bash
sudo mv ./kubectl-podsinfops  /usr/local/bin
```

You can now use the plugin, kubectl podsinfop, for the default namespace and Kubectl podsinfop mynamespace for a specific namespace.

**And what about Windows?**

There are two main problems, PowerShell files are not directly executable and filename extensions.
To address the first problem, you need to use a .bat file to call your Powershell script. 
The first step create a folder for plugin and add it to your Path environment variable. 

Then, create your script here but do not name it kubectl-xxx, use another naming convention like K8splugin-scriptname.ps1. 
In the directory plugin create a kubectl-podinfops.bat and add this command. 

```
@echo off 
pwsh.exe  -file "x:\pathTo\K8splugin-podinfosps.ps1" %1
```
Now you can use the plugin, kubectl podinfops.bat. But wait, there is a little more, you can omit the .dat of the file name, kubectl podinfops.

Creating plugins for kubectl is simple. Plugins work on Linux and Windows. Now, you know how you can use your PowerShell skills to automate your kubectl tasks.