PowerFlex: Setting up PowerFlex single sign-on with Microsoft Azure using OpenID Connect (OIDC)
Summary: This article walks you through configuring PowerFlex Customer Identity and Access Management (CIAM) for SSO with Microsoft Azure Entra ID using OIDC.
Instructions
This article walks you through configuring PowerFlex Customer Identity and Access Management (CIAM) for SSO with Microsoft Azure Entra ID using OIDC, including certificate setup, IdP and service provider (SP) configuration, token exchange, and API access validation. This configuration makes it possible for you to take advantage of SSO capabilities. It requires configuration changes on both PowerFlex and Microsoft Azure.
This article is intended for administrators and security personnel using PowerFlex.
PowerFlex CIAM integrates with OIDC-compliant IdPs, including Microsoft Azure Entra ID, to enable centralized authentication and secure API access.
This article provides a step-by-step process to set up PowerFlex single sign-on with Microsoft Azure through the REST API. Here is the list of high-level steps:
- Initialize CIAM and configure PowerFlex as an OIDC Service Provider (SP)
- Register Microsoft Azure Entra ID as an OIDC Identity Provider (IdP)
- Add the required CA certificates (DigiCert/GlobalSign) to CIAM
- Configure Microsoft Azure application claims, redirect URIs, and permissions
- Create CIAM OAuth2 clients and token exchange mappings
- Obtain a Microsoft Azure token, exchange it for a PowerFlex token, and validate REST API access
Terminology
The following table describes important terms and concepts needed to complete this process:
| Term | Description |
| Management virtual machine (MVM) IP address | The MVM IP addresses hosting PowerFlex Manager and management services. One MVM storage data server (SDS) node You can find the MVM IP addresses, by logging into the PowerFlex Manager user interface. Go to System->Components>MVM. For example, 10.2xx.3xx.184, 10.2xx.3xx.185, 10.2xx.3xx.186 |
| Storage data server (SDS) | The storage data server (SDS) node that is hosting the MVMs. |
| Ingress IP address | The external IP for rke2-ingress-nginx-controller (load balancer/ingress endpoint) |
| Customer Identity and Access Management (CIAM) | The PowerFlex Customer Identity and Access Management services for OAuth2/OIDC |
| Service provider (SP) | Identity provider (IdP)|The service provider (PowerFlex/Keycloak side) and the identity provider (Microsoft Azure Entra ID) |
Prerequisites
There is an automation script (oidc_azure.tar) that is used for connecting Microsoft Azure Entra ID with PowerFlex.
The script is in a tar file format and must be downloaded and copied to your MVM:
To copy the oidc_azure. tar file, you must have administrative access to the PowerFlex management virtual machine.
Here is the file: oidc_azure.tar
You must run the commands in the screen capture below because the variables that are exported are reused across the steps and scripts in this process.
sudo -i

What each command do?
- The `
kubectl get svc -A | grep "sso " | awk '{print $4}'`command returns the cluster IP address of the SSO service in the Kubernetes cluster. - The `
curl -k --location --request POST "https://${SSO_IP}:8080/rest/auth/login" --header 'Accept: application/json' --header 'Content-Type: application/json' --data '{"username": "admin","password": "Scaleio123!" }' | jq -r .access_token` command logs into PowerFlex using the SSO REST API and retrieves an access token (JWT) that is used for subsequent API calls to PowerFlex services. - The`
kubectl get svc -A | grep -m1 rke2-ingress-nginx-controller | sort | awk '{print $5}'`command returns the external IP address of the rke2-ingress-nginx-controller service, which is typically the load balancer or ingress controller IP used for external traffic.
When these commands are run, the following variables are exported:
export SSO_IP=$(kubectl get svc -A | grep "sso " | awk '{print $4}')
export ASMUI_PASS=$(kubectl get secret pfxm-asmui-creds -o json -n powerflex \ | jq '.data | map_values(@base64d)' \ | jq -r '.["keycloak-password"]')
export PM_TOKEN=$(curl -k --location --request POST "https://${SSO_IP}:8080/rest/auth/login" \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --data "{\"username\": \"asmuiuser\",\"password\": \"$ASMUI_PASS\"}" | jq -r .access_token)
export IN_IP=$(kubectl get svc -A | grep -m1 rke2-ingress-nginx-controller | sort | awk '{print $5}')
1. Initialize CIAM and configure PowerFlex as an OIDC Service Provider (SP)
This step is the starting point in the process of integrating CIAM with PowerFlex using OpenID Connect (OICD). The SSO CIAM configuration for PowerFlex is initialized.
API: POST /rest/v1/sso-ciam/init
Command:
curl -k -X POST https://$IN_IP/rest/v1/sso-ciam/init --header 'Accept: application/json' --header 'Content-Type: application/json' --header "Authorization: Bearer ${PM_TOKEN}"
Script: ./init.sh
Output: An integer, the CIAM tenant ID i.e. "2"
2. Configure PowerFlex with Keycloak as the OIDC Service provider.
This step registers and configures PowerFlex as an OIDC Service Provider with Keycloak (or another IdP), enabling secure SSO authentication using OpenID Connect.
API operation: POST /rest/v1/oidc-sp-config
Command:
curl -kL -X POST --url https://$IN_IP/rest/v1/oidc-sp-config --header 'Content-Type: application/json' --header "Authorization: Bearer ${PM_TOKEN}" --data '{
"sp_id": "powerflex-$IN_IP",
"redirect_uri": "https://$IN_IP/auth/realms/powerflex/protocol/openid-connect/auth",
"logout_uri": "https://$IN_IP/auth/realms/powerflex/protocol/openid-connect/logout",
"required_claims": ["email"],
"keycloak_settings": {
"config": {
"clientAuthMethod": "client_secret_basic",
"pkceEnabled": "true",
"useJwksUrl": "true",
"validateSignature": "true"
},
"first_broker_login_flow_alias": "first broker login",
"post_broker_login_flow_alias": null,
"link_only": null,
"store_token": true,
"add_read_token_role_on_create": true,
"trust_email": true
},
"days_to_store_state_code_verifier": 1}'
Script: ./add_oidc_sp.sh
Output (truncated): New service provider
{"id":"00000000-0000-0000-0000-000000000000","sp_id":"powerflex-10.247.39.179","redirect_uris":["https://10.247.39.179/auth/realms/powerflex/protocol/openid-connect/auth"],"logout_uri":"https://10.247.39.179/auth/realms/powerflex/protocol/openid-connect/logout" ... },
2.1 List the OIDC service provider.
This step retrieves the current OIDC service provider configuration from PowerFlex,
Command:
curl -kL https://$IN_IP/rest/v1/oidc-sp-config --header 'Content-Type: application/json' --header "Authorization: Bearer ${PM_TOKEN}" | jq -r '.results[]'
Script: ./list_oidc_sp.sh
Sample output (truncated): A service provider created at the previous step.
{"results":[{"id":"00000000-0000-0000-0000-000000000000","sp_id":"powerflex-10.247.39.179",...}]
3. Add the certificate authority to PowerFlex for CIAM services.
This step involves informing PowerFlex about which certificate authority it should trust when validating secure communication with the CIAM system. There are two certificates for CIAM services:
- DigiCert is a trusted Certificate Authority (CA). Microsoft Azure services (like Microsoft Azure AD, OIDC endpoints, and Microsoft APIs) use SSL/TLS certificates issued by DigiCert to secure communication.
- GA2 (GlobalSign or similar root/intermediate) certificates are part of the certificate chain that validates Microsoft Azure’s identity endpoints. They ensure:
- The OIDC metadata URL (https://login.microsoftonline.com/...)(External Link) and token endpoints are trusted.
- Secure HTTPS communication between PowerFlex and Microsoft Azure IdP.
This operation ensures CIAM trusts Microsoft Azure endpoints.
The command below uses files with PEM encoded certificates, where new lines are replaced with "\n". Basically, there is only one line in such file.
For example:
-----BEGIN CERTIFICATE-----
\nMIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\nd3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\nQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\nMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\nb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\nCSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\nnh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\nT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\ngdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\nBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\nTLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\nDQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\nhMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\nPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\nYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\nCAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n-
----END CERTIFICATE-----
API: POST /Api/V1/CIAM/<ciam_id>/x509-certificates
Command:
CA=`cat $1`
curl -kvvL -X POST https://$IN_IP//Api/V1/CIAM/<return_value_from_init_ciam>/x509-certificates --header "Authorization: Bearer ${PM_TOKEN}" --data-raw "
{
\"type\": \"CA\",
\"service\": \"ALL\",
\"certificate_format\": \"PEM\",
\"certificate\": \"$CA\"
}"
Script: ./add_cert.sh <certificate_pem_file>
Sample output (truncated):
cat digicert_ca.pem
-----BEGIN CERTIFICATE-----\nMIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\nMQs;wCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\nd3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\nQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\nMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\nb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\nCSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\nnh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\nT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\ngdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\nBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\nTLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\nDQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\nhMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\nPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\nYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4p
...
Fsiv9kuXclVzDAGySj4dzp30d8tbQk\nCAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n-----END CERTIFICATE-----\n
delladmin@lglou184:~/oidc_testing> cat digicert_g2.pem
-----BEGIN CERTIFICATE-----\nMIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\nd3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\nMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\nMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\nb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\nq2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\ntCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\nvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\nBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\nNeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\nFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\npLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\nMrY=\n-----END CERTIFICATE-----\n
--------------------------------------------------------------------------------------------------------------------------
./add_cert.sh digicert_ca.pem
{"id":"b5c3d51a-9161-4515-a741-4a197a348cf9","certificate":"-----BEGIN CERTIFICATE-----...\nMIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\nMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\nd3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\nQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\nMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\nb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\nCSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\nnh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\nT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\ngdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\nBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\nTLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\nDQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\nhMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\nPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\nYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\nCAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n-----END CERTIFICATE-----\n","subject":"C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA","common_name":"DigiCert Global Root CA","issuer":"C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root CA","organization":"DigiCert Inc","organizational_unit":"www.digicert.com","country":"US","valid_from_timestamp":"2006-11-10T00:00:00Z","valid_to_timestamp":"2031-11-10T00:00:00Z","key_usage":"Digital Signature, Cert Sign, CRL Sign","type":"CA","service":"ALL","is_self_signed":true,"is_current":true,"is_valid":true,"cert_fingerprint":"43:48:A0:E9:44:4C:78:CB:26:5E:05:8D:5E:89:44:B4:D8:4F:96:62:BD:26:DB:25:7F:89:34:A4:43:C7:* Connection #0 to host 10.247.39.179 left intact
01:61","version":3,"serial_number":10944719598952040374951832963794454346,"signature_algorithm":"RSA","signature_hash_algorithm":"SHA-1","public_key":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKP\nC3eQyaKl7hLOllsBCSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtx\nRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmF\naG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvU\nX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrT\nC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOv\nJwIDAQAB\n-----END PUBLIC KEY-----\n"}
3.1 Verify the certificates are added and listed.
This step verifies that the certificates are add and listed.
Command:
curl -kL https://$IN_IP/Api/V1/CIAM/2/x509-certificates --header 'Content-Type: application/json' --header "Authorization: Bearer ${PM_TOKEN}"
Note: https://$IN_IP//Api/V1/CIAM/2/x509-certificate - here is the number 2 from the init.sh in step 1.
Script: ./list_certs.sh
Sample output (truncated):
{"results":[{"id":"4a4d7f6b-251c-41a6-b304-783ed6d5fbc2","certificate":"-----BEGIN ....-----END PUBLIC KEY-----\n"}]}
4. Register Microsoft Azure Entra ID as the OIDC Service (IdP)
This step registers Microsoft Azure Entra ID as an external identity provider for PowerFlex using OpenID Connect (OIDC), enabling users to authenticate through Microsoft Azure rather than relying on local PowerFlex credentials.
Variables:
- Tenant ID: The Tenant ID identifies the Microsoft Azure Active Directory tenant to which the identity provider belongs. You can find it on the Overview page of the application's home screen in Microsoft Azure.
- KK_ID: The KK ID identifies the client ID of the Microsoft Azure application.
- KK_SECRET: The KK secret identifies the client secret of Microsoft Azure.
API: POST /rest/v1/oidc-services
curl -kvvL --request POST \
--url "https://$IN_IP/rest/v1/oidc-services" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${PM_TOKEN}" \
--data "{
\"name\": \"azure\",
\"is_enabled\": true,
\"idp_metadata_url\": \"https://login.microsoftonline.com/<Tenant_id>/v2.0/.well-known/openid-configuration\",
\"claims_mapper\": [
{ \"name\": \"email\", \"value\": \"email\" },
{ \"name\": \"given_name\", \"value\": \"firstName\" },
{ \"name\": \"preferred_username\", \"value\": \"username\" },
{ \"name\": \"family_name\", \"value\": \"lastName\" }
],
\"client_id\": \"$KK_ID\",
\"client_secret\": \"$KK_SECRET\",
\"scopes\": [\"openid\",\"profile\",\"email\",\"offline_access\"],
\"pkce_enabled\": true,
\"code_challenge_method\": \"S256\",
\"idp \"idp_type\": \"AzureEntraID\"KK_ID=b151947c-b514-4997-a369-a9b76f26b050
KK_SECRET=tk_8Q~QYTj1FQYN2chWovu.M5Ph3YnIVzLwRJcoZ
TENANT_ID=945c199a-83a2-4e80-9f8c-5a91be5752dd
curl -kL -X POST --url https://$IN_IP/rest/v1/oidc-services --header 'Content-Type: application/json' --header "Authorization: Bearer ${PM_TOKEN}" \
--data \
"{ \"name\": \"azure\", \"is_enabled\": true, \"idp_metadata_url\": \"https://login.microsoftonline.com/${TENANT_ID}/v2.0/.well-known/openid-configuration\",
\"claims_mapper\": [ { \"name\": \"email\", \"value\": \"email\"
},
{
\"name\": \"given_name\",
\"value\": \"firstName\"
},
{
\"name\": \"preferred_username\",
\"value\": \"username\"
},
{
\"name\": \"family_name\",
\"value\": \"lastName\"
}
],
\"client_id\": \"$KK_ID\",
\"client_secret\": \"$KK_SECRET\",
\"scopes\": [
\"openid\",
\"profile\",
\"email\",
\"offline_access\"
],
\"pkce_enabled\": true,
\"code_challenge_method\": \"S256\",
\"idp_type\": \"AzureEntraID\"
}"
Script: ./add_oidc_service.sh
Sample output (truncated):
{
"id": "a230b7b8-8f6c-47db-9327-2e48db8040a9",
"name": "azure",
"is_enabled": true,
"idp_metadata_url": "https://login.microsoftonline.com/945c199a-83a2-4e80-9f8c-5a91be5752dd/v2.0/.well-known/openid-configuration",
"idp_metadata_load_timestamp": "2025-11-14T16:00:40.356569216Z",
"idp_metadata": {
"issuer": "https://login.microsoftonline.com/945c199a-83a2-4e80-9f8c-5a91be5752dd/v2.0",
"...": "additional metadata fields"
},
"code_challenge_method": "S256",
"idp_type": "AzureEntraID",
"is_default": false
}
4.1 List the OIDC service.
This step lists the OIDC service.
Command:
curl -kL https://$IN_IP/rest/v1/oidc-services --header 'Content-Type: application/json' --header "Authorization: Bearer ${PM_TOKEN}"
Script: list_oidc_service.sh
Example (truncated):
./list_oidc_service.sh
{"results":[{"id":"a230b7b8-8f6c-47db-9327-2e48db8040a9","name":"azure","is_enabled":true,"idp_metadata_url":"https://login.microsoftonline.com/945c199a-83a2-4e80-9f8c-5a91be5752dd/v2.0/.well-known/openid-configuration",..."idp_type":"AzureEntraID","is_default":false}]}
5. Configure the redirect URL in Microsoft Azure.
This step involves setting up Microsoft Azure to send users to a destination called a redirect URL after they successfully sign in.
In the Microsoft Azure Portal, go to App Registration → Authentication → Redirect URIs, and add the following URL:
https://<PFMP_IP>/auth/realms/powerflex/broker/<SERVICE_ID_AZURE>/endpoint
For example: https://10.247.39.179/auth/realms/powerflex/broker/a230b7b8-8f6c-47db-9327-2e48db8040a9/endpoint
- <PFMP_IP> is the PowerFlex Manager IP (if required, resolve hostname)
- <SERVICE_ID_AZURE> is the IdP from the service ID from Step 4.
6. Configure the token claims in Microsoft Azure.
a) Add optional claims (ID Token)
- In the Microsoft Azure Portal, go to Token configuration → Add optional claim.
- Under Token type, select ID.
- Under Claim, select the checkbox beside the following attributes:
-
email
-
family_name
-
given_name
-
preferred_username
-
- Click Add.
b) Add group claims
- In the Microsoft Azure Portal, go to Token configuration → Add group claims.
- Under Edit groups claim, select the checkbox beside Groups assigned to application.
- Under ID, select the checkbox beside Group ID.
- Under Access, select the checkbox beside Group ID.
- Click Add.
You will use these group IDs later for PowerFlex role mapping in the PowerFlex Manager user interface.
7. Configure API permissions in Microsoft Azure.
This step ensures that PowerFlex has the correct access to protected resources in Microsoft Azure.
- In the Microsoft Azure Portal, go to API permissions →Microsoft Graph.
- Under Request API permissions, select
- Under Request API permissions, select User → Read.
- Click Update permissions.
8. (Optional) Assign users/groups in Microsoft Azure.
This step grants the appropriate individuals or teams access to PowerFlex by assigning them to specific roles or groups within Microsoft Azure Active Directory.
- In the Microsoft Azure Portal, go to Enterprise applications → Users and groups.
- Under Display name, select the checkbox beside the user/group and copy the object ID.
The object ID is used for claims-based role mapping in PowerFlex.
9. (Optional) Add the user/group in the PowerFlex user interface
This step involves granting access within PowerFlex by adding the appropriate user or group directly in the PowerFlex Manager user, ensuring they can log in and use the system according to their assigned roles and permissions.
- Log in to the PowerFlex Manager user interface.
- Go to Settings → User management → Add Remote User/Group.
- Enter the following information:
- Type: group
- Provider: Azure
- Group name: any meaningful name
- Claim Name: groups
- User role: Super User
- Claim Value: Object ID from Microsoft Azure (Step 8)
10. Add the client to CIAM.
This step adds the client to CIAM to register PowerFlex so CIAM can determine what application is requesting authentication, where to send tokens, what permissions the application needs, and which authentication flows it can use.
API: POST /rest/v1/oauth2-clients
Command:
curl -kL --request POST \
--url "https://$IN_IP/rest/v1/oauth2-clients" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${PM_TOKEN}" \
--data "{
\"client_name\": \"azure_oidc_client\",
\"redirect_uris\": [\"\"],
\"authorization_code_flow\": false,
\"client_credentials_flow\": false,
\"token_exchange_enabled\": true,
\"offline_access_enabled\": true,
\"client_offline_session_idle\": 1,
\"client_offline_session_max\": 1,
\"client_security_level\": \"TRUSTED\"
Script: ./addclient_ciam.sh
Example output:
./addclient_ciam.sh
{"id":"77ce10cb-c40e-4c1e-b922-07014b5b6e7b","client_name":"azure_oidc_client","client_secret":"QXDtyVYO5l3Puj2ZMACz/MK+sTSOqDHy53oRhVkXiBc=","redirect_uris":[""],"authorization_code_flow":false,"client_credentials_flow":false,"signing_algorithm":"RS256","refresh_token_max_reuse":3,"access_token_expiration_duration":360,"token_exchange_enabled":true,"client_security_level":"TRUSTED","offline_access_enabled":true,"client_offline_session_idle":1,"client_offline_session_max":1
10.1 Verify the client is added by listing it.
This step verifies that the client is successfully added by listing all the existing clients.
Command:
curl -kL https://$IN_IP/rest/v1/oauth2-clients --header 'Content-Type: application/json' --header "Authorization: Bearer ${PM_TOKEN}" | jq -r '.results[]'
Script: ./list_clients_ciam.sh
Example:
./list_clients_ciam.sh
{
"id": "77ce10cb-c40e-4c1e-b922-07014b5b6e7b",
"client_name": "azure_oidc_client",
"redirect_uris": [
""
],
"authorization_code_flow": false,
"client_credentials_flow": false,
"signing_algorithm": "RS256",
"refresh_token_max_reuse": 3,
"access_token_expiration_duration": 360,
"token_exchange_enabled": true,
"client_security_level": "TRUSTED",
"offline_access_enabled": true,
"client_offline_session_idle": 1,
"client_offline_session_max": 1,
"num_failed_login_attempts": 0
}
10.2 Modify the client.
This step updates an existing login client in CIAM. The payload should remain empty ({}). This command adds PowerFlex required mappings to Keycloak client.
Command:
curl -k -X PATCH "https://$IN_IP/rest/v1/login-clients/$1" \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer ${PM_TOKEN}" \
--data '{}'
Script: ./update_client.sh <ID_FROM_ADD_CLIENT_CIAM>
Example:
./update_client.sh 77ce10cb-c40e-4c1e-b922-07014b5b6e7b
{"id":"1c3dbd45-66bc-44c7-94ce-1aef3a282afc","name":"azure_oidc_client","client_id":"77ce10cb-c40e-4c1e-b922-07014b5b6e7b","is_enabled":true,"client_secret":null,"redirect_uris":[""],"web_origins":[""]}
11. Add the application to CIAM for token exchange mapping
This step adds the application to CIAM to enable external authentication, authorization, and secure identity flows.
API: POST /rest/v1/oauth2-token-exchanges
Required variables:
- LOCAL_CLIENT_ID: ID from addclient_ciam.sh (e.g., 77ce10cb-c40e-4c1e-b922-07014b5b6e7b)
- EXT_CLIENT_ID: Microsoft Entra ID Application (client) ID (e.g., 859c0721-cb0c-433c-a437-bd2d2462754f)
- METADATA: Microsoft Azure application metadata URL (e.g., https://login.microsoftonline.com/<Tenant_id>/v2.0/.well-known/openid-configuration)
- IDP: OIDC service ID (e.g., a230b7b8-8f6c-47db-9327-2e48db8040a9)
- ROLE: PowerFlex role (e.g., SuperUser)
- PM_TOKEN: API bearer token
- IN_IP: Target host/IP address
Command:
curl -kL -X POST "https://$IN_IP/rest/v1/oauth2-token-exchanges" \
-H "Content-Type: application/json" \
-H "clientId: $LOCAL_CLIENT_ID" \
-H "Authorization: Bearer ${PM_TOKEN}" \
--data @- <<EOF
{
"ciam_oauth2_client_id": "$LOCAL_CLIENT_ID",
"customer_client_id": "$EXT_CLIENT_ID",
"customer_metadata_url": "$METADATA",
"idp_service_id": "$IDP",
"static_roles": ["$ROLE"]
}
Script: ./add_app.sh
11.1 List the application ID
This step retrieve the application ID to validate the unique client identifier required for downstream authorization and token‑exchange operations.
Command:
curl -kL "https://$IN_IP/rest/v1/oidc-services" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer ${PM_TOKEN}"
Script:
./list_apps.sh
Example:
./list_apps.sh
{ "id": "f803da04-2979-49dc-9408-496f127639b4",
"ciam_oauth2_client_id": "77ce10cb-c40e-4c1e-b922-07014b5b6e7b",
"customer_client_id": "859c0721-cb0c-433c-a437-bd2d2462754f",
"customer_metadata_url": "https://login.microsoftonline.com/945c199a-83a2-4e80-9f8c-5a91be5752dd/v2.0/.well-known/openid-configuration",
"idp_service_id": "a230b7b8-8f6c-47db-9327-2e48db8040a9",
"static_roles": [
"SuperUser" ]
}
12. Map roles in Keycloak (PowerFlex Realm)
This step maps roles in the PowerFlex Keycloak realm to align identity provider role assignments with PowerFlex authorization requirements.
1. Log in to Keycloak using the following endpoint: https://<PFMP_IP>/auth/admin/
For example: https://10.247.39.179/auth/admin/
To log in to the Keycloak, the credentials can be retrieved as below
Command:
kubectl get secret keycloak-admin-credentials -o json -n powerflex | jq '.data | map_values(@base64d)'
Script: ./get_keycloak_creds.sh
Example:
./get_keycloak_creds.sh
{
"password": "sZFmHmfhjj",
"username": "keycloak"
}
2. In the Keycloak instance, select the PowerFlex.
3. Click on Users and select the user ID and update the attributes to match the role used in CIAM (for example, SuperUser).
4. Under Role mapping, verify that the role is updated.
13. Obtain the Microsoft Azure token (Resource Owner Password Credentials / username + password)
There are multiple ways to get a token from Microsoft Azure AD. In this step example, the application is logging in to Microsoft Entra ID (Azure AD) by directly submitting a user’s username + password to the token endpoint in order to receive an OAuth 2.0 token. This is the Resource Owner Password Credentials (ROPC) grant.
Command:
curl -X POST https://login.microsoftonline.com/$TENANT_ID/oauth2/token -H 'Content-Type: application/x-www-form-urlencoded' -d "grant_type=password&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&username=$USERNAME&password=$encoded_password&resource=$RESOURCE")
Script:./get_azure_token.sh
Tip: To ensure that the token is valid, go to the JSON Web Token (JWT) Debugger (External Link)
14. Exchange the Microsoft Azure token for the PowerFlex token (CIAM)
This step exchanges the Microsoft Azure token for a PowerFlex CIAM token so that the PowerFlex APIs can be called.
API: POST /rest/v1/token
Variables:
- CIAM_CLIENT_ID - Client ID from Add the client to CIAM step (step 10)
- CIAM_CLIENT_SECRET - Client secret from Add the client to CIAM step (step 10)
- EXT_TOKEN - Token from Microsoft Azure
Command:
curl -kL -X POST https://$IN_IP/rest/v1/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header "ClientId: $CIAM_CLIENT_ID" \
--header "ClientSecret: $CIAM_CLIENT_SECRET" \
--data "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
--data "subject_token=${EXT_TOKEN}" \
--data "subject_token_type=urn:ietf:params:oauth:token-type:jwt"
Scripts: ./token.sh (uses the EXT_TOKEN therefore set the environmental variable accordingly)
Example output (truncated):
{
"access_token": "ey...A",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "ey...qA"
}
15. Verify access to the PowerFlex REST API
This step confirms that the token works by calling a PowerFlex REST endpoint. It uses the access_token from the output of the "token.sh", exported as env variable PM_TOKEN.
export PM_TOKEN=eyJhb...eN_A
Command:
curl -k --location --request GET https://$IN_IP/Api/V1/Credential --header 'Content-Type: application/json' --header "Authorization: Bearer $PM_TOKEN"
{
"totalRecords" : 3,
"credentialList" : [ {
"link" : {
"title" : "root",
"href" : "getCredential/69087b0c-cce7-4be7-b154-e8a3019cc553",
"rel" : "self",
"type" : null
},
"credential" : {
"type" : "OSCredential",
"id" : "69087b0c-cce7-4be7-b154-e8a3019cc553",
....
"link" : null,. "link" : null,. "link" : null, "references" : {
"devices" : 0,
"policies" : 0
}
} ]
}
Reference information
Summary of API sequence
- Initialize CIAM for PowerFlex
(/rest/v1/sso-ciam/init) - Configure PowerFlex as OIDC service provider
(/rest/v1/oidc-sp-config) - Add certificates for CIAM Services
(DigiCert, GlobalSign, etc.) - Register Microsoft Azure Entra ID as OIDC Service
(/rest/v1/oidc-services) - Configure Service ID in Microsoft Azure Authentication tab
(Broker endpoint mapping) - Set token and group claims in Microsoft Azure
(Optional claims, API permissions) - Add OAuth2 client in CIAM
(/rest/v1/oauth2-clients) - Add application to CIAM for token exchange
(/rest/v1/oauth2-token-exchanges) - Obtain Microsoft Azure token to exchange for PowerFlex token
(/rest/v1/token) - Verify access to PowerFlex REST APIs
Commands to perform a token exchange
Calling PowerFlex APIs with Microsoft Azure Entra token
Command to perform a token exchange:
curl -kL -X POST https://$PFMP_IP/rest/v1/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header "ClientId: $CIAM_CLIENT_ID" \
--header "ClientSecret: $CIAM_CLIENT_SECRET" \
--data "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
--data "subject_token=${AZURE_TOKEN}" \
--data "subject_token_type=urn:ietf:params:oauth:token-type:jwt" | jq '.'
Example:
curl -kL -X POST https://10.247.39.179/rest/v1/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header "ClientId: 77ce10cb-c40e-4c1e-b922-07014b5b6e7b" \
--header "ClientSecret: QXDtyVYO5l3Puj2ZMACz/MK+sTSOqDHy53oRhVkXiBc=" \
--data "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
--data "subject_token=${AZURE_TOKEN}" \
--data "subject_token_type=urn:ietf:params:oauth:token-type:jwt" | jq '.'
Calling PowerFlex Manager Appliance API with exchanged token
curl -kL 'https://10.247.39.179/api/v1/appliance' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzMkNrM2YwNEI5OG5pRENza19IVmZ3R..._dxDp9QGWdteptby-8aaA'
Calling PowerFlex Block API with exchanged token
curl -kL GET https://10.247.39.179/api/types/Volume/instances --header 'Content-Type: application/json' --header "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzMkNrM2YwNEI5OG5pRENza19IVmZ3R..._dxDp9QGWdteptby-8aaA "
[
{
"genType": "EC",
"mappedSdcInfo": [
{
"limitIops": null,
"limitBwInMbps": null,
"isDirectBufferMapping": null,
"sdcIp": "",
"sdcId": "15d47a2400010000",
"sdcName": "513fda498aaf35e883da34f6ac46480",
"nqn": "nqn.2014-08.org.nvmexpress:uuid:72f30442-1cf7-0f5b-129c-da4fae6a028b",
"accessMode": "ReadWrite",
"hostType": "NVMeHost"
}
],
"managedBy": "ScaleIO",
"originalExpiryTime": 0,
"retentionLevels": [],
"snplIdOfSourceVolume": null,
"volumeReplicationState": "UnmarkedForReplication",
"replicationJournalVolume": false,
"replicationTimeStamp": 0,
"name": "csivol-21ef5b460c",
"creationTime": 1763382058,
"storagePoolId": "0ebdb61000000000",
"dataLayout": "ErasureCoding",
"vtreeId": "97957f7d00000002",
"sizeInKb": 8388608,
"compressionMethod": "NotApplicable",
"volumeClass": "defaultclass",
"accessModeLimit": "ReadWrite",
"lockedAutoSnapshotMarkedForRemoval": false,
"snplIdOfAutoSnapshot": null,
"pairIds": null,
"useRmcache": false,
"volumeType": "ThinProvisioned",
"consistencyGroupId": null,
"ancestorVolumeId": null,
"notGenuineSnapshot": false,
"secureSnapshotExpTime": 0,
"lockedAutoSnapshot": false,
"autoSnapshotGroupId": null,
"timeStampIsAccurate": false,
"nsid": 3,
"id": "77d698a400000002",
"links": [
{
"rel": "self",
"href": "/api/instances/Volume::77d698a400000002"
},
{
"rel": "/dtapi/rest/v1/metrics/query",
"href": "/dtapi/rest/v1/metrics/query",
"body": {
"resource_type": "volume",
"ids": [
"77d698a400000002"
]
}
},
{
"rel": "/api/parent/relationship/vtreeId",
"href": "/api/instances/VTree::97957f7d00000002"
},
{
"rel": "/api/parent/relationship/storagePoolId",
"href": "/api/instances/StoragePool::0ebdb61000000000"
}
]
},
{
"genType": "EC",
"mappedSdcInfo": null,
"managedBy": "ScaleIO",
"originalExpiryTime": 0,
"retentionLevels": [],
"snplIdOfSourceVolume": null,
"volumeReplicationState": "UnmarkedForReplication",
"replicationJournalVolume": false,
"replicationTimeStamp": 0,
"name": "csivol-4ea9633592",
"creationTime": 1763380456,
"storagePoolId": "0ebdb61000000000",
"dataLayout": "ErasureCoding",
"vtreeId": "9795586d00000000",
"sizeInKb": 8388608,
"compressionMethod": "NotApplicable",
"volumeClass": "defaultclass",
"accessModeLimit": "ReadWrite",
"lockedAutoSnapshotMarkedForRemoval": false,
"snplIdOfAutoSnapshot": null,
"pairIds": null,
"useRmcache": false,
"volumeType": "ThinProvisioned",
"consistencyGroupId": null,
"ancestorVolumeId": null,
"notGenuineSnapshot": false,
"secureSnapshotExpTime": 0,
"lockedAutoSnapshot": false,
"autoSnapshotGroupId": null,
"timeStampIsAccurate": false,
"nsid": 1,
"id": "77d6719400000000",
"links": [
{
"rel": "self",
"href": "/api/instances/Volume::77d6719400000000"
},
{
"rel": "/dtapi/rest/v1/metrics/query",
"href": "/dtapi/rest/v1/metrics/query",
"body": {
"resource_type": "volume",
"ids": [
"77d6719400000000"
]
.
.
.
]
}
]