import requests
url = "https://platform.ankra.app/org/clusters/aws/preflight"
payload = {
"name": "<string>",
"credential_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"ssh_key_credential_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"region": "<string>",
"bastion_allowed_ips": ["<string>"]
}
headers = {
"cookie": "ankra_session=",
"X-Ankra-CSRF": "<x-ankra-csrf>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
cookie: 'ankra_session=',
'X-Ankra-CSRF': '<x-ankra-csrf>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: '<string>',
credential_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
ssh_key_credential_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
region: '<string>',
bastion_allowed_ips: ['<string>']
})
};
fetch('https://platform.ankra.app/org/clusters/aws/preflight', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));curl --request POST \
--url https://platform.ankra.app/org/clusters/aws/preflight \
--header 'Content-Type: application/json' \
--header 'X-Ankra-CSRF: <x-ankra-csrf>' \
--cookie ankra_session= \
--data '
{
"name": "<string>",
"credential_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"ssh_key_credential_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"region": "<string>",
"bastion_allowed_ips": [
"<string>"
]
}
'{
"items": [
{
"check": "<string>",
"status": "ok",
"message": "<string>"
}
],
"can_proceed": true,
"network_ownership": "created",
"resolved_egress_mode": "nat_gateway",
"resolved_availability_zones": [
"<string>"
]
}{
"detail": "Cluster not found"
}{
"detail": "Cluster not found"
}{
"detail": "Cluster not found"
}{
"detail": "Cluster not found"
}{
"detail": "Cluster not found"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}{
"detail": "Cluster not found"
}Preflight an AWS EC2 cluster create (browser session)
Browser session authentication; RBAC permission clusters.create. Runs every check the create runs against AWS without creating anything. Each item is three-state: ok, warning (a read Ankra could not perform, or a caveat such as flannel not enforcing the IMDS guard), or error. network_ownership says which shape the request describes; a created network is checked for its CIDR plan (cidr), its zones (zones - chosen here when the request names none), the VPC, NAT-gateway and elastic-IP quotas, while an adopted VPC is checked for its VPC, subnets, egress routes and bastion subnet. resolved_egress_mode is the mode the create will run under, null when it could not be decided; resolved_availability_zones the zones the cluster will spread over. Answers 404 while the organisation’s aws_provider feature flag is off.
import requests
url = "https://platform.ankra.app/org/clusters/aws/preflight"
payload = {
"name": "<string>",
"credential_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"ssh_key_credential_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"region": "<string>",
"bastion_allowed_ips": ["<string>"]
}
headers = {
"cookie": "ankra_session=",
"X-Ankra-CSRF": "<x-ankra-csrf>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
cookie: 'ankra_session=',
'X-Ankra-CSRF': '<x-ankra-csrf>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: '<string>',
credential_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
ssh_key_credential_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
region: '<string>',
bastion_allowed_ips: ['<string>']
})
};
fetch('https://platform.ankra.app/org/clusters/aws/preflight', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));curl --request POST \
--url https://platform.ankra.app/org/clusters/aws/preflight \
--header 'Content-Type: application/json' \
--header 'X-Ankra-CSRF: <x-ankra-csrf>' \
--cookie ankra_session= \
--data '
{
"name": "<string>",
"credential_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"ssh_key_credential_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"region": "<string>",
"bastion_allowed_ips": [
"<string>"
]
}
'{
"items": [
{
"check": "<string>",
"status": "ok",
"message": "<string>"
}
],
"can_proceed": true,
"network_ownership": "created",
"resolved_egress_mode": "nat_gateway",
"resolved_availability_zones": [
"<string>"
]
}{
"detail": "Cluster not found"
}{
"detail": "Cluster not found"
}{
"detail": "Cluster not found"
}{
"detail": "Cluster not found"
}{
"detail": "Cluster not found"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}{
"detail": "Cluster not found"
}Authorizations
Browser session. Mutations also require X-Ankra-CSRF.
Headers
Must match the ankra_csrf browser cookie.
Body
POST /clusters/aws body. Two network shapes (ADR 0015 §3): omit vpc_id and Ankra creates the VPC from network_ip_range over availability_zones (one public /24 and one private /20 per zone, an internet gateway, and NAT gateways as the default egress), all torn down with the cluster; pass vpc_id with node_subnet_ids and bastion_subnet_id to adopt an existing VPC, whose network Ankra never modifies. Nodes never receive a public IP; the bastion holds the elastic IP and is the SSH hop (and the NAT instance in bastion_nat mode).
An organisation aws credential: a keys credential, or a role onboarded with scope provisioning or self_managed (cost-scoped roles are refused).
Region slug, validated against ec2:DescribeRegions.
IPv4 CIDRs allowed to SSH to the bastion. Optional: omitted or empty means 0.0.0.0/0 (open to everyone; key-only, password login off, sshd rate-limited on the host). Name your own CIDRs to restrict it; preflight reports the exposure as bastion_ssh_exposure.
An existing VPC to adopt; node_subnet_ids and bastion_subnet_id are then required and network_ip_range / availability_zones / nat_gateway_single_zone are refused. Omitted (or empty): Ankra creates the VPC (network_ownership created).
Adopted VPC only: private subnets the nodes spread across; one or more, their zones become the cluster's zone pool. Refused (422) when no vpc_id is given - a created VPC lays out its own subnets.
1Adopted VPC only: a public subnet (internet-gateway default route) for the bastion. Refused (422) when no vpc_id is given.
Created VPC only: the IPv4 CIDR the VPC is created with, /16 to /20 as a network address; it must hold a private /20 and a public /24 per availability zone (the preflight's cidr item says what it holds). Refused (422) alongside vpc_id.
Created VPC only: the zones the VPC is laid out over, in the order the spread walks them (the bastion and the first control plane land in the first). Each must exist and be available in the region; more than one needs control_plane_count >= 3. Omitted: the preflight picks one zone when control_plane_count < 3 and three otherwise, from the region's available zones in name order, and reports them as resolved_availability_zones. Refused (422) alongside vpc_id.
Created VPC with nat_gateway egress only: false creates one NAT gateway (and elastic IP) per zone; true creates one in the first zone that every private subnet routes through - cheaper, one egress failure domain. Refused (422) when true alongside vpc_id.
Which modes apply depends on the network shape. Created VPC: nat_gateway (default; Ankra-created NAT gateways with their own elastic IPs) or bastion_nat (the bastion is the NAT instance); existing is refused (422). Adopted VPC: omitted is resolved by preflight; existing keeps the subnets' NAT routing; bastion_nat makes the bastion the NAT instance behind one Ankra-owned route table and is refused when a node subnet carries instances Ankra did not create; nat_gateway is refused (422) because it would re-point the customer's route tables.
nat_gateway, bastion_nat, existing At least 3 when the cluster spans more than one availability zone (an adopted VPC's node subnets, or a created VPC's availability_zones); it also decides how many zones a created VPC defaults to (1 below 3, 3 otherwise).
1 <= x <= 90 <= x <= 100Show child attributes
Show child attributes
k3s, kubeadm stacked, external Defaults to cilium for both distributions: the AWS stack's IMDS guard is a network policy only Cilium and Calico enforce. flannel is accepted with a preflight warning; kubeadm requires cilium.
flannel, calico, cilium Show child attributes
Show child attributes
arm64 is refused with a 422 naming the pending image-catalogue audit (ADR 0015 §6).
amd64 Encrypted gp3 root volume of every instance.
20 <= x <= 2000GitOps provider of the repository the cluster is bootstrapped onto. Omitted is github, where gitops_repository is owner/name. bitbucket_cloud names the repository by gitops_workspace and gitops_repo_slug (or gitops_repository as workspace/repo_slug) and binds it as a Bitbucket Cloud repository.
github, bitbucket_cloud Bitbucket Cloud workspace of the GitOps repository. Only with gitops_provider bitbucket_cloud; provided together with gitops_repo_slug.
Bitbucket Cloud repository slug of the GitOps repository. Only with gitops_provider bitbucket_cloud; provided together with gitops_workspace.
delete, retain Must be true when present: the AWS cloud-controller-manager is mandatory.
Response
Successful response
Show child attributes
Show child attributes
The network shape the request describes: created when it names no vpc_id, adopted otherwise.
created, adopted The egress mode the create will run under; null when it could not be decided.
nat_gateway, bastion_nat, existing The zones the cluster spreads over: a created VPC's planned zones (the request's, or the ones the preflight chose), an adopted VPC's node-subnet zones. Empty until they are known.