title: Kubernetes Service Account for Jobs
body: <p>I have a requirement to invoke a Kubernetes job from another pod in the same namespace in a RBAC enabled cluster. The job is created using the C#  <a href="https://github.com/kubernetes-client/csharp" rel="nofollow noreferrer">Kubernetes client library</a>. This task works correctly on a test environment that does not have RBAC enabled.  </p>
  
  <p>After following the <a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/" rel="nofollow noreferrer">Kubernetes documentation</a> for creating a Service Account, I have the following YAML.</p>
  
  <pre><code>apiVersion: v1
  kind: ServiceAccount
  metadata:
    name: x20-jobs-sp
    namespace: prod
  ---
  kind: ClusterRole
  apiVersion: rbac.authorization.k8s.io/v1
  metadata:
    namespace: prod
    name: x20-jobs-sp-role
  rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: ["batch", "extensions"]
    resources: ["jobs"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  ---
  apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRoleBinding
  metadata:
    name: x20-jobs-sp-rolebinding
    namespace: prod
  subjects:
    - kind: ServiceAccount
      # Reference to ServiceAccount kind's `metadata.name`
      name: x20-jobs-sp
      # Reference to ServiceAccount kind's `metadata.namespace`
      namespace: prod
  roleRef:
    kind: ClusterRole
    name: x20-jobs-sp-role
    apiGroup: rbac.authorization.k8s.io
  </code></pre>
  
  <p>This is the code that invokes the job.</p>
  
  <pre class="lang-cs prettyprint-override"><code>var job = await client.CreateNamespacedJobAsync(new k8s.Models.V1Job
  {
      Metadata = new k8s.Models.V1ObjectMeta
      {
          Name = safeName
      },
      Spec = new k8s.Models.V1JobSpec
      {
          Template = new k8s.Models.V1PodTemplateSpec
          {
              Spec = new k8s.Models.V1PodSpec
              {
                  Containers = new List&lt;k8s.Models.V1Container&gt;()
                  {
                      new k8s.Models.V1Container
                      {
                          Image = $"{_containerRegistry}/{jobName}:{_imageTag}",
                          Args = args.Select(x =&gt; x.ToString()).ToList(),
                          Env = GetDefaultEnvironment(),
                          Name = safeName,
                          ImagePullPolicy = "Always"  
                      }
                  },
                  ImagePullSecrets = new List&lt;k8s.Models.V1LocalObjectReference&gt;
                  {
                      new k8s.Models.V1LocalObjectReference
                      {
                          Name = _containerRegistry
                      }
                  },
                  RestartPolicy = "OnFailure", 
                  ServiceAccountName = "x20-jobs-sp" // jobs service principal
              }
          },
          TtlSecondsAfterFinished = _ttlSecondsAfterFinished
      }
  }, _namespace);
  
  </code></pre>
  
  <p>This correctly create's the ServiceAccount, ClusterRole and ClusterRoleBinding, however attempting to invoke the task still result's in a <code>Forbidden</code> result.</p>
  
  <p>I've attempted a number of different options for the ClusterRole including using the built in <code>cluster-admin</code> role, all without success. This has been tested using the <code>kubectl auth can-i</code> command.</p>
  
  <p>What am I missing?</p>