<p>Kubernetes v1.20.0 , on Ubuntu 20.04.1, docker 19.3.11</p>
<p>Using the following configuration I am able to create a deployment in the namespace &quot;live&quot; using this serviceaccount's token, but I am unable to delete the deployment using the same token.</p>
<p>Trying to find the correct Role to bind to my programmatic ServiceAccount user, but I am unable to get the correct role mapping for deployment deletion.  I've tried with and without slashes, asterisks in the resource names, and pluralizing the Resources.</p>
<p>Here's the error i get from the API:</p>
<pre><code>  warnings.warn(
Exception (403)
Reason: Forbidden
HTTP response headers: HTTPHeaderDict({'Cache-Control': 'no-cache, private', 'Content-Type': 'application/json', 'X-Content-Type-Options': 'nosniff', 'X-Kubernetes-Pf-Flowschema-Uid': 'xxxx-bf2f-43b3-af5d-3ce15b086080', 'X-Kubernetes-Pf-Prioritylevel-Uid': 'xxxx-0d42-4b08-820e-396249f74107', 'Date': 'Mon, 28 Dec 2020 13:33:00 GMT', 'Content-Length': '408'})
HTTP response body: {&quot;kind&quot;:&quot;Status&quot;,&quot;apiVersion&quot;:&quot;v1&quot;,&quot;metadata&quot;:{},&quot;status&quot;:&quot;Failure&quot;,&quot;message&quot;:&quot;deployments.apps \&quot;live-stream-deploy-testing123\&quot; is forbidden: User \&quot;system:serviceaccount:live:live-serviceaccount\&quot; cannot delete resource \&quot;deployments\&quot; in API group \&quot;apps\&quot; in the namespace \&quot;live\&quot;&quot;,&quot;reason&quot;:&quot;Forbidden&quot;,&quot;details&quot;:{&quot;name&quot;:&quot;live-stream-deploy-testing123&quot;,&quot;group&quot;:&quot;apps&quot;,&quot;kind&quot;:&quot;deployments&quot;},&quot;code&quot;:403}
</code></pre>
<p>Here is the deployment i'm trying to delete:</p>
<pre><code>$ kubectl -n live get deploy
NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
live-stream-deploy-testing123   1/1     1            1           76m
</code></pre>
<p>Here's where i'm currently at with my role definition ( I have tried with asterisks in Resource Names as well this did not work for create, many iterations later:):</p>
<pre><code>$ kubectl -n live describe role
Name:         live-serviceaccount-role
Labels:       &lt;none&gt;
Annotations:  &lt;none&gt;
PolicyRule:
  Resources          Non-Resource URLs  Resource Names  Verbs
  ---------          -----------------  --------------  -----
  deployment/        []                 []              [get watch create list delete]
  deployment         []                 []              [get watch create list delete]
  deployments/       []                 []              [get watch create list delete]
  deployments        []                 []              [get watch create list delete]
  service/           []                 []              [get watch create list delete]
  service            []                 []              [get watch create list delete]
  services/          []                 []              [get watch create list delete]
  services           []                 []              [get watch create list delete]
  services           []                 []              [get watch create list delete]
  deployment.apps/   []                 []              [get watch create list delete]
  deployment.apps    []                 []              [get watch create list delete]
  deployments.apps/  []                 []              [get watch create list delete]
  deployments.apps   []                 []              [get watch create list delete]
  service.apps/      []                 []              [get watch create list delete]
  service.apps       []                 []              [get watch create list delete]
  services.apps/     []                 []              [get watch create list delete]
  services.apps      []                 []              [get watch create list delete]
</code></pre>
<p>Role binding:</p>
<pre><code>$ kubectl -n live describe rolebindings
Name:         live-serviceaccount-rolebinding
Labels:       &lt;none&gt;
Annotations:  &lt;none&gt;
Role:
  Kind:  Role
  Name:  live-serviceaccount-role
Subjects:
  Kind            Name                 Namespace
  ----            ----                 ---------
  ServiceAccount  live-serviceaccount  live
</code></pre>
<p>pip package kubernetes==12.0.1.  I have not tested it against a previous version.
Script that results in error:</p>
<pre><code>import kubernetes.client
from kubernetes.client.rest import ApiException
import logging
logger = logging.getLogger(__name__)

DEPLOYMENT_NAME = &quot;live-stream-deploy-testing123&quot; 

configuration = kubernetes.client.Configuration()
configuration.api_key['authorization'] = &quot;service account token here...&quot;
configuration.api_key_prefix['authorization'] = 'Bearer'
configuration.verify_ssl = False

configuration.host = &quot;https://my_k8s_API:6443&quot;

with kubernetes.client.ApiClient(configuration) as api_client:

    try:
        api_instance = kubernetes.client.AppsV1Api(api_client)
        api_instance.delete_namespaced_deployment(
            name=DEPLOYMENT_NAME, namespace=&quot;live&quot;)
        logger.info(&quot;Deployment deleted. %s&quot; % DEPLOYMENT_NAME)

    except ApiException as e:
        logger.error(&quot;Exception %s\n&quot; % e)
</code></pre>
<p>Some additional &quot;can-i&quot; action:</p>
<pre><code>$ kubectl -n live auth can-i  --as=system:serviceaccount:live:live-serviceaccount delete deploy
yes
$ kubectl auth can-i --as=system:serviceaccount:live:live-serviceaccount  --list -n live
Resources                                       Non-Resource URLs                     Resource Names   Verbs
selfsubjectaccessreviews.authorization.k8s.io   []                                    []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                                    []               [create]
deployment/                                     []                                    []               [get watch create list delete]
deployment                                      []                                    []               [get watch create list delete]
deployments/                                    []                                    []               [get watch create list delete]
deployments                                     []                                    []               [get watch create list delete]
service/                                        []                                    []               [get watch create list delete]
service                                         []                                    []               [get watch create list delete]
services/                                       []                                    []               [get watch create list delete]
services                                        []                                    []               [get watch create list delete]
deployment.apps/                                []                                    []               [get watch create list delete]
deployment.apps                                 []                                    []               [get watch create list delete]
deployments.apps/                               []                                    []               [get watch create list delete]
deployments.apps                                []                                    []               [get watch create list delete]
service.apps/                                   []                                    []               [get watch create list delete]
service.apps                                    []                                    []               [get watch create list delete]
services.apps/                                  []                                    []               [get watch create list delete]
services.apps                                   []                                    []               [get watch create list delete]
                                                [/.well-known/openid-configuration]   []               [get]
                                                [/api/*]                              []               [get]
                                                [/api]                                []               [get]
                                                [/apis/*]                             []               [get]
                                                [/apis]                               []               [get]
                                                [/healthz]                            []               [get]
                                                [/healthz]                            []               [get]
                                                [/livez]                              []               [get]
                                                [/livez]                              []               [get]
                                                [/openapi/*]                          []               [get]
                                                [/openapi]                            []               [get]
                                                [/openid/v1/jwks]                     []               [get]
                                                [/readyz]                             []               [get]
                                                [/readyz]                             []               [get]
                                                [/version/]                           []               [get]
                                                [/version/]                           []               [get]
                                                [/version]                            []               [get]
                                                [/version]                            []               [get]
</code></pre>
<p>Here are the YAMLs that produced the above outputs.  They are all on the live namespace.  Forgive the mess on the roles, but I've been trying to iterate through to a solution:</p>
<pre><code>$ cat role.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: live-serviceaccount-role
  namespace: live
rules:
- apiGroups: [&quot;&quot;,&quot;apps&quot;] 
  resources: [&quot;services&quot;, &quot;services/&quot;, &quot;deployment&quot;, &quot;deployment/&quot;, &quot;service&quot;, &quot;service/&quot;, &quot;deployments&quot;, &quot;deployments/&quot;]
  resourceNames: [&quot;&quot;]
  verbs: [&quot;get&quot;, &quot;watch&quot;, &quot;create&quot;, &quot;list&quot;, &quot;delete&quot;]
- apiGroups: [&quot;&quot;] 
  resources: [&quot;services&quot;]
  resourceNames: [&quot;&quot;]
  verbs: [&quot;get&quot;, &quot;watch&quot;, &quot;create&quot;, &quot;list&quot;, &quot;delete&quot;]

$ cat serviceaccount.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: live-serviceaccount
  namespace: live

$ cat rolebinding.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: live-serviceaccount-rolebinding
  namespace: live
subjects:
- kind: ServiceAccount
  name: live-serviceaccount # &quot;name&quot; is case sensitive
  namespace: live
roleRef:
  kind: Role #this must be Role or ClusterRole
  name: live-serviceaccount-role # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: &quot;rbac.authorization.k8s.io&quot;
</code></pre>
<p>Here is a sample of the script that successfully lights up the deployment in the live namespace:</p>
<pre><code>import kubernetes.client
from kubernetes import client
import logging
logger = logging.getLogger(__name__)

DEPLOYMENT_NAME = &quot;live-stream-deploy-testing123&quot;
POD_APP_LABEL = &quot;lab&quot;

configuration = kubernetes.client.Configuration()

# Configure API key authorization: BearerToken
configuration.api_key['authorization'] = &quot;service account token here...&quot;
# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed
configuration.api_key_prefix['authorization'] = 'Bearer'
configuration.verify_ssl = False

# Defining host is optional and default to http://localhost
configuration.host = &quot;https://&lt;myk8sapi&gt;:6443&quot;

# Enter a context with an instance of the API kubernetes.client
#with kubernetes.client.ApiClient(configuration) as api_client:
with kubernetes.client.ApiClient(configuration) as api_client:

    api_instance = kubernetes.client.AppsV1Api(api_client)

    def create_deployment_object():
        # Configureate Pod template container
        container = kubernetes.client.V1Container(
            name=&quot;lab-container&quot;,

            # redacted for privacy
            image=&quot;nginx:latest&quot;,

            termination_message_path=&quot;/var/log/messages&quot;,
            image_pull_policy=&quot;IfNotPresent&quot;,
            resources=client.V1ResourceRequirements(
                requests={&quot;memory&quot;: &quot;5Gi&quot;},
                limits={&quot;memory&quot;: &quot;16Gi&quot;}
            ),
            volume_mounts=[
                client.V1VolumeMount(mount_path=&quot;/etc/nginx/nginx.conf.template&quot;, name=&quot;nginxconftemplate&quot;, sub_path=&quot;nginx.conf&quot;)
            ]
        )
        volume1 = client.V1Volume(
            name=&quot;nginxconftemplate&quot;,
            config_map=client.V1ConfigMapVolumeSource(
                name=&quot;live-nginx-conf&quot;,
                default_mode=0o0777
            )
        )
        # Create and configurate a spec section
        template = client.V1PodTemplateSpec(
            metadata=client.V1ObjectMeta(labels={&quot;app&quot;: POD_APP_LABEL}),
            spec=client.V1PodSpec(containers=[container],
                                  volumes=[volume1],
                                  node_selector={&quot;backend&quot;: &quot;yes&quot;},
                                  )
        )
        # Create the specification of deployment
        spec = client.V1DeploymentSpec(
            replicas=1,
            template=template,
            selector={'matchLabels': {'app': POD_APP_LABEL}})
        # Instantiate the deployment object
        deployment = client.V1Deployment(
            api_version=&quot;apps/v1&quot;,
            kind=&quot;Deployment&quot;,
            metadata=client.V1ObjectMeta(name=DEPLOYMENT_NAME),
            spec=spec)

        return deployment

    def create_deployment(api_instance, deployment):
        # Create deployement
        api_response = api_instance.create_namespaced_deployment(
            body=deployment,
            namespace=&quot;live&quot;)
        logger.info(&quot;Deployment created. status='%s'&quot; % str(api_response.status))

    deployment = create_deployment_object()
    create_deployment(api_instance, deployment)


</code></pre>

Patrick Scott Best
<p>While your answer covers the solution it does not explain why the new set of roles that you provided are working successfully in contrast to the previous ones.</p>
<p>The <code>can-i</code> command was a bit misleading in this case. Result of it, was in fact correct since with this role you were able to delete but only chosen/mentioned resources. Since you used  <code>resourceNames</code> with an empty list <code>[&quot;&quot;]</code>, your role did not allow to delete anything.</p>
<p>To simply test this I placed the exact deployment name in the <code>resourceNames</code>:</p>
<pre class="lang-yaml prettyprint-override"><code>apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: live-serviceaccount-role
  namespace: live
rules:
- apiGroups: [&quot;&quot;,&quot;apps&quot;] 
  resources: [&quot;services&quot;, &quot;services/&quot;, &quot;deployment&quot;, &quot;deployment/&quot;, &quot;service&quot;, &quot;service/&quot;, &quot;deployments&quot;, &quot;deployments/&quot;]
  resourceNames: [&quot;live-stream-deploy-testing123&quot;]
  verbs: [&quot;get&quot;, &quot;watch&quot;, &quot;create&quot;, &quot;list&quot;, &quot;delete&quot;]
</code></pre>
<p>Once I did that I launched your code and deployment was successfully deleted.
So to sum up You should either fill the <code>resourceNames</code> with names of resources or omit it completely.</p>
