ONTAP automation hub
Choose the interface that matches the job: Ansible for convergent configuration, Terraform for lifecycle-managed infrastructure, PowerShell for Windows operations, and Python or direct REST for custom workflows. All current paths converge on ONTAP's HTTPS REST API; keep legacy ZAPI/ONTAPI only while a required operation still lacks a supported replacement.
The automation landscape
| Tool | Best fit | State model |
|---|---|---|
Ansible netapp.ontap | Repeatable configuration and orchestration | Module compares desired and actual state |
| NetApp ONTAP Terraform provider | Provisioned objects owned by a Terraform state | Plan/state lifecycle |
| PowerShell Toolkit | Interactive and scheduled Windows administration | Imperative cmdlets |
netapp-ontap Python client | Applications, checks, custom workflow logic | Code-defined |
| REST/curl | Debugging and thin integrations | Caller-defined |
REST details, pagination and asynchronous jobs live in the ONTAP REST API guide; this page focuses on tool choice and production patterns.
REST is the foundation
ONTAP resources are below https://cluster-mgmt/api, not /api/v1. The endpoint's OpenAPI UI is normally at /docs/api. Basic authentication is supported; beginning with ONTAP 9.14, OAuth 2.0 bearer tokens come from an external authorization server. /api/security/authentication/login is not the documented ONTAP token-issuance workflow—check your product and release if an example shows it.
export ONTAP_HOST=cluster1.example.com
export ONTAP_USER=svc_automation
read -s ONTAP_PASSWORD
curl --fail --silent --show-error --user "$ONTAP_USER:$ONTAP_PASSWORD" \
"https://$ONTAP_HOST/api/storage/volumes?name=app_data&fields=name,size,state,svm"
{"records":[{"name":"app_data","size":107374182400,"state":"online","svm":{"name":"svm_app"}}],"num_records":1}
curl --fail --silent --show-error \
-H "Authorization: Bearer $ONTAP_TOKEN" -H "Accept: application/json" \
"https://$ONTAP_HOST/api/cluster?fields=name,version"
{"name":"cluster1","version":{"full":"NetApp Release 9.19.1"}}Use a trusted CA bundle; -k is acceptable only for an isolated first test. REST has evolved by ONTAP release through resource and field additions rather than a public v1/v2 URL switch. Inspect the target cluster's API documentation and release notes, request only needed fields, and treat deprecated ONTAPI/ZAPI calls as migration work.
Ansible: convergent configuration
Install the official collection, keep connection values in inventory or encrypted variables, and prefer REST-capable modules. The collection documents na_ontap_volume, na_ontap_svm, na_ontap_lun and na_ontap_snapmirror.
$ ansible-galaxy collection install netapp.ontap
Starting galaxy collection install process
netapp.ontap:23.1.0 was installed successfully
# inventory/host_vars/cluster1.yml — password supplied by Vault
ansible_host: cluster1.example.com
ontap_username: svc_ansible
ontap_password: "{{ vault_ontap_password }}"
ontap_https: true
ontap_validate_certs: true
- name: Build application storage
hosts: ontap
gather_facts: false
collections: [netapp.ontap]
tasks:
- name: Ensure SVM exists
na_ontap_svm:
state: present
name: svm_app
root_volume: svm_app_root
root_volume_aggregate: aggr1
hostname: "{{ ansible_host }}"
username: "{{ ontap_username }}"
password: "{{ ontap_password }}"
https: true
validate_certs: true
use_rest: always
- name: Ensure 500 GiB volume exists
na_ontap_volume:
state: present
vserver: svm_app
name: app_data
aggregate_name: aggr1
size: 500
size_unit: gb
junction_path: /app_data
space_guarantee: none
hostname: "{{ ansible_host }}"
username: "{{ ontap_username }}"
password: "{{ ontap_password }}"
https: true
validate_certs: true
use_rest: always
- name: Ensure database LUN and mirror exist
block:
- netapp.ontap.na_ontap_lun:
state: present
vserver: svm_app
name: /vol/app_data/db01
size: 200
size_unit: gb
ostype: linux
hostname: "{{ ansible_host }}"
username: "{{ ontap_username }}"
password: "{{ ontap_password }}"
https: true
validate_certs: true
- netapp.ontap.na_ontap_snapmirror:
state: present
source_endpoint: {cluster: cluster1, path: "svm_app:app_data"}
destination_endpoint: {cluster: cluster2, path: "svm_dr:app_data_dp"}
schedule: hourly
policy: MirrorAndVault
hostname: cluster2.example.com
username: "{{ ontap_username }}"
password: "{{ ontap_password }}"
https: true
validate_certs: true
rescue:
- debug: {msg: "Provisioning failed; preserve task output and REST error for triage"}Terraform: state-owned infrastructure
The verified Registry source is NetApp/netapp-ontap; resource names use the netapp-ontap_ prefix. Provider releases change schema, so pin a reviewed version and consult that version's Registry documentation.
terraform {
required_providers {
netapp-ontap = { source = "NetApp/netapp-ontap", version = "~> 2.0" }
}
}
provider "netapp-ontap" {
connection_profiles = [{
name = "prod"
hostname = var.ontap_host
username = var.ontap_username
password = var.ontap_password
validate_certs = true
}]
}
resource "netapp-ontap_svm" "app" {
cx_profile_name = "prod"
name = "svm_app"
ipspace = "Default"
aggregates = [{ name = "aggr1" }]
}
resource "netapp-ontap_volume" "data" {
cx_profile_name = "prod"
name = "app_data"
svm_name = netapp-ontap_svm.app.name
aggregates = [{ name = "aggr1" }]
space_guarantee = "none"
space = { size = 500, size_unit = "gb", percent_snapshot_space = 5 }
nas = { junction_path = "/app_data", security_style = "unix" }
}Import existing objects before managing them, review every plan, store state in an encrypted locked backend, and never let two tools own the same setting.
PowerShell Toolkit
The Data ONTAP PowerShell Toolkit exposes familiar Nc cmdlets. Exact parameters vary by toolkit and ONTAP version; use Get-Help ... -Full before production scripting.
$cred = Get-Credential svc_automation
$nc = Connect-NcController -Name cluster1.example.com -HTTPS -Credential $cred
Get-NcVol -Controller $nc -Vserver svm_app -Name app_data |
Select-Object Name, State, TotalSize, Available
Name State TotalSize Available
---- ----- --------- ---------
app_data online 536870912000 498216206336
New-NcVol -Controller $nc -VserverContext svm_app \
-Name logs -Aggregate aggr1 -Size 100GB -JunctionPath /logs
Name State Aggregate Size
---- ----- --------- ----
logs online aggr1 107374182400Python client library
The netapp-ontap package wraps REST resources and exceptions. Pin a version compatible with the ONTAP releases you manage.
from netapp_ontap import HostConnection
from netapp_ontap.resources import Volume
from netapp_ontap.error import NetAppRestError
with HostConnection("cluster1.example.com", username=user, password=password,
verify=True):
try:
for vol in Volume.get_collection(name="app_data", fields="name,size,state"):
print(vol.name, vol.size, vol.state)
except NetAppRestError as err:
raise SystemExit(f"ONTAP request failed: {err}")Idempotency, errors and scale
- Read, compare, change: PATCH only fields that differ; accept success on a desired state already present.
- Jobs: a 202 response may carry a job link. Poll it to success or failure instead of treating acceptance as completion.
- Errors: log HTTP status, ONTAP error code/message and correlation context, but redact authorization headers.
- Load: request specific fields, paginate collections, cache UUID lookups, limit concurrency, honor
429/Retry-Afterif returned, and use exponential backoff with jitter for transient 429/5xx responses. Published limits can vary—measure and check your version. - Ownership: define which system owns each object; drift correction is dangerous when Terraform, Ansible and humans fight.
CI/CD and where automation fits
Good targets are configuration management, compliance drift reports, repeatable provisioning and backup/replication orchestration. Destructive recovery and security changes should retain approval gates.
name: ontap-config
on: [pull_request, workflow_dispatch]
jobs:
check:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- run: ansible-playbook -i inventory/prod.yml storage.yml --check --diff
env:
ANSIBLE_VAULT_PASSWORD_FILE: ${{ secrets.VAULT_PASSWORD_FILE }}
- if: github.event_name == 'workflow_dispatch'
run: ansible-playbook -i inventory/prod.yml storage.yml
env:
ANSIBLE_VAULT_PASSWORD_FILE: ${{ secrets.VAULT_PASSWORD_FILE }}Use an isolated runner with management-network reachability, environment-protected secrets, least-privilege ONTAP RBAC, pinned actions/collections/providers, change review and an audit trail.