← ALL DOCS
DOCS · PLATFORM TEAMS

Installation guide

This guide is for the platform team installing FunctionPilot in their AWS account. The install is one CloudFormation stack (the hub). Every other account whose functions FunctionPilot should reach gets a small role stack (the spoke). With IAM Identity Center or another SAML IdP, the hub takes two steps: deploy the stack, configure the SAML application, then update the stack. For the short version, see Getting started.

Before you start

  • Region. Any Region with AWS Lambda, Amazon API Gateway (HTTP APIs), Amazon DynamoDB, Amplify Hosting, Amazon Cognito, AWS License Manager and Amazon ECS on Fargate. The templates are always loaded from the vendor's public bucket in us-east-1 (CloudFormation accepts a template URL from any Region), and all product code comes from the container image. The hub's Region doesn't limit which Regions' functions you can see.
  • Account. Deploy the hub in the account that subscribed on AWS Marketplace: the licence is delivered to that account through AWS License Manager. To run the hub in another account of your organization, distribute the licence to it with a License Manager grant first.
  • Permissions. You need permission to create the resources in the stack: IAM roles (some have fixed names), Lambda, ECS, EC2 security groups, Cognito, API Gateway, Amplify, DynamoDB, S3, SNS, EventBridge, KMS and CloudWatch Logs.
  • Network. A VPC with at least one subnet with outbound HTTPS access, for the one-off installer task (see Networking). Nothing else runs in your VPC.
  • Identity. Decide how users will sign in: IAM Identity Center, another SAML 2.0 IdP (Entra ID, Okta and others), an OIDC IdP, or Cognito-managed users. SAML IdPs other than IAM Identity Center, and OIDC, need the Team plan or above.

1. Subscribe

  1. Open FunctionPilot in AWS Marketplace and choose Continue to Subscribe. Choose the plan and the contract length (Team is the base plan; Business and Enterprise are add-ons to it) and accept the offer. AWS License Manager now holds your licence.
  2. Choose Continue to Configuration, select the fulfilment option and the Region, then open the CloudFormation launch link from the usage instructions.

2. Launch the hub stack

In the CloudFormation console, fill in the parameters (see the reference). Acknowledge "AWS CloudFormation might create IAM resources with custom names" and create the stack. Creation takes about 10 minutes.

The quick-create link, with the release version filled in:

https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?templateURL=https://cloudcertainty-functionpilot-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/main.yaml&stackName=functionpilot

From the AWS CLI (--region is the Region you deploy to):

bash
aws cloudformation create-stack --region <your-region> --stack-name functionpilot \
  --template-url https://cloudcertainty-functionpilot-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/main.yaml \
  --parameters file://params.json \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM

params.json lists the parameters as [{"ParameterKey": "VpcId", "ParameterValue": "vpc-..."}, ...].

What happens during creation

The product's code (the Lambda functions and the web app) ships inside the subscription-protected container image, not as public downloads. Early in stack creation:

  1. CloudFormation creates a private S3 bucket in your account (output ArtifactsBucketName; all public access blocked, encrypted, TLS only).
  2. The ArtifactInstall custom resource starts one Fargate task from the product image in your InstallerSubnetIds. The task checks your AWS Marketplace licence in AWS License Manager, verifies every file of the code bundle against the SHA-256 checksums in the image, and copies it into the bucket under the release's version. It runs for about a minute at 0.25 vCPU and 512 MB, with a read-only root filesystem and a security group without inbound rules.
  3. The Lambda functions and the web app are then created from that bucket.

If the installer task fails, stack creation fails and rolls back. The error message gives the task's exit code and its log stream (log group /<NamePrefix>/installer); see Troubleshooting.

3. Connect your identity provider

IdentityProviderWhat to do
IAMIdentityCenterFollow Identity providers. Create the app with the SamlAcsUrl and SamlAudienceUri outputs, then update the stack with SamlMetadataUrl.
SAMLTeam and above. Do the same in your IdP (Entra ID and Okta are covered in Identity providers).
OIDCTeam and above. Put the client secret in Secrets Manager, then set OidcIssuer, OidcClientId and OidcClientSecretArn when you create the stack. Register <CognitoDomain>/oauth2/idpresponse as the redirect URI in your IdP.
CognitoOnlyNothing more to do. BootstrapAdminEmail receives a temporary password. Every user must register an authenticator app (TOTP MFA is required).

Until SamlMetadataUrl is set, the portal falls back to Cognito sign-in, which has no users. Nobody can sign in at that point, so the stack is safe in that state.

4. First sign-in

Open the PortalUrl output and sign in as BootstrapAdminEmail. The first account that signs in with that (verified) email becomes the bootstrap administrator and stays bound to it (see the admin guide).

The local connection to the hub's own account exists from the start. It uses the stack's local role, which may do what LocalAccessLevel allows, in the Regions of LocalRegions. Administrators see every function there. Other users see nothing until an administrator grants them access (admin guide).

5. Deploy the spoke role in other accounts

spoke-role.yaml creates one role, /functionpilot/<RoleName>, that only the hub's API role (stack output SpokeTrustedPrincipal) can assume, only with your external ID, and only with a user's email as the source identity. Its template URL is the stack output SpokeTemplateUrl:

https://cloudcertainty-functionpilot-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/spoke-role.yaml
ParameterDefaultMeaning
TrustedPrincipalThe hub's SpokeTrustedPrincipal output (one role ARN)
ExternalIdA secret of 16 to 128 characters (letters, digits and +=,.@:/-). Enter the same value when you add the connection in FunctionPilot
RoleNamefunctionpilot-spokeRole name; the role is created under the IAM path /functionpilot/, the only path the hub may assume
AccessLevelReadOnlyReadOnly (list functions, read configuration, metrics and logs), Operate (plus test invocations) or Full (plus configuration, environment variables, versions, aliases and concurrency)

The role is the upper limit of what FunctionPilot can do in that account; grants narrow it per user or group but never widen it. The role has no permission to change code, delete functions, or touch IAM, CloudFormation, DynamoDB or S3. IAM is global, so deploy the stack once per account, in any one Region. The stack's outputs are RoleArn (enter it in the connection) and AccessLevel (choose the same level for the connection).

A single account

bash
cat > spoke-params.json <<'EOF'
[
  {"ParameterKey": "TrustedPrincipal", "ParameterValue": "<SpokeTrustedPrincipal output>"},
  {"ParameterKey": "ExternalId", "ParameterValue": "<a secret of at least 16 characters>"},
  {"ParameterKey": "AccessLevel", "ParameterValue": "ReadOnly"}
]
EOF
aws cloudformation create-stack --region us-east-1 --stack-name functionpilot-spoke \
  --template-url https://cloudcertainty-functionpilot-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/spoke-role.yaml \
  --parameters file://spoke-params.json --capabilities CAPABILITY_NAMED_IAM

Administration › Connections › How to connect another AWS account in the portal shows the same steps with your template URL.

Every account of an AWS Organization (StackSets)

With service-managed StackSets, new accounts in the target OUs get the role automatically. Use one external ID for the whole StackSet, and enter that same external ID for every connection you create for those accounts.

bash
# From the management account or a delegated StackSets administrator.
aws cloudformation create-stack-set --region us-east-1 --stack-set-name functionpilot-spoke \
  --template-url https://cloudcertainty-functionpilot-us-east-1.s3.us-east-1.amazonaws.com/pro/<version>/spoke-role.yaml \
  --parameters file://spoke-params.json \
  --capabilities CAPABILITY_NAMED_IAM \
  --permission-model SERVICE_MANAGED \
  --auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false

# One Region only: the role is global.
aws cloudformation create-stack-instances --region us-east-1 --stack-set-name functionpilot-spoke \
  --deployment-targets OrganizationalUnitIds=ou-abcd-11111111 --regions us-east-1

Service-managed StackSets don't deploy to the management account; use a normal stack there if FunctionPilot should reach it. To change AccessLevel later, update the StackSet, then the access level of the connections.

Add the connection

In Administration › Connections › Add connection, enter an id (used in links, it can't be changed later), a name, the account ID, the access level, the spoke stack's RoleArn, the external ID and the Regions to show. Choose Test: FunctionPilot assumes the role and lists the functions in each Region. See Connections.

Parameter reference

ParameterDefaultDescription
NamePrefixfunctionpilot2 to 20 lowercase letters, digits and hyphens, starting with a letter. It prefixes resource and role names, so it must be unique in the account and Region. Don't use aws, amazon or cognito: it forms part of the Cognito domain.
TagOwnerplatform-teamValue of the Owner tag on every taggable resource.
TagCostCenternoneValue of the CostCenter tag on every taggable resource.
TagEnvironmentprodValue of the Environment tag on every taggable resource.
IdentityProviderIAMIdentityCenterIAMIdentityCenter, SAML, OIDC or CognitoOnly. SAML (other than IAM Identity Center) and OIDC need the Team plan or above. See Identity providers.
SamlMetadataUrlemptyThe metadata URL of your SAML application (IAMIdentityCenter or SAML only). Leave it empty on the first deployment.
OidcIssueremptyOIDC only, required: the issuer URL.
OidcClientIdemptyOIDC only, required: the client ID.
OidcClientSecretArnemptyOIDC only, required: the ARN of a Secrets Manager secret whose value is the client secret. CloudFormation resolves it at deploy time.
OidcEmailVerifiedClaimemail_verifiedOIDC only: the IdP claim that says the email is verified. Use xms_edov for Microsoft Entra ID.
BootstrapAdminEmailThe first administrator. With CognitoOnly the user is created and receives a temporary password by email.
LocalAccessLevelOperateThe most FunctionPilot may do with the functions in this account, for every user: ReadOnly, Operate or Full, as the spoke role's AccessLevel.
LocalRegionsemptyComma-separated Regions whose functions to show for this account, for example eu-west-1,us-east-1. Empty means the stack's Region only.
IdentityCenterStartUrlemptyOptional. Your AWS access portal URL (https://<id>.awsapps.com/start). When set, people who sign in through IAM Identity Center work with their own permission sets instead of grants. Team plan and above. See Personal AWS access.
IdentityCenterRegionus-east-1The Region of your IAM Identity Center instance.
PersonalAccessKeysDisabledEnabled lets users enter their own IAM user access keys, which FunctionPilot exchanges for temporary credentials and never stores. Team plan and above.
VpcIdVPC for the one-off installer task.
InstallerSubnetIdsSubnets for the installer task, in VpcId (a template rule checks it), with outbound HTTPS (see Networking).
InstallerSubnetsArePublicfalsetrue only for subnets that route to an internet gateway; the task then gets a public IP for outbound access.
LogRetentionDays90Retention of FunctionPilot's own CloudWatch log groups, including the audit events written there.
KmsKeyArnemptyOptional customer managed KMS key (see Encryption). Empty uses AWS owned keys and SSE-S3.
UpdateChecksEnabledOnce a day, read the public release manifest from the vendor's bucket and announce a newer release to administrators and on the update SNS topic. Nothing about you, your users or your account is sent.
UpdateNotificationEmailemptyOptional email address subscribed to the update topic.
ArtifactVersionOverride, ImageUriOverrideemptyAdvanced (support use only). Leave empty unless Cloud Certainty support asks you to set them.

The IdentityCenterStartUrl, IdentityCenterRegion and PersonalAccessKeys parameters set the first values of the personal access settings; administrators can change them later in Administration › Settings.

Networking

Only the installer task runs in your VPC, for about a minute at install and at each upgrade. It needs outbound HTTPS (443) to:

  • Amazon ECR in us-east-1, to pull the image from the AWS Marketplace registry (cross-Region, so this needs internet egress: a NAT gateway or public subnets);
  • AWS License Manager in your Region, for the licence check;
  • Amazon S3 in your Region, to write the code into the artifacts bucket (a gateway endpoint works);
  • CloudWatch Logs in your Region, for its log stream.

Its security group has no inbound rules and one outbound rule, TCP 443. The Lambda functions don't run in your VPC; they reach Lambda, CloudWatch and STS in your accounts over AWS's public endpoints.

Encryption and KMS

Without KmsKeyArn, the DynamoDB tables, the log groups, the update SNS topic and the artifacts bucket use AWS owned keys and SSE-S3. The stack still creates one small customer managed KMS key with automatic rotation for personal AWS access: it encrypts the Identity Center tokens and temporary credentials people connect. Only the API role may use it, and only with an encryption context that names the user. The key is deleted with the stack (after a 7-day waiting period).

With KmsKeyArn, your key encrypts the DynamoDB tables, CloudWatch Logs, the SNS topic, the artifacts bucket and the personal access secrets, and the stack creates no key of its own. The key must be a symmetric customer managed key in the stack's Region (give its ARN, not an alias), and its key policy must:

  • let the account's IAM policies grant its use (the default key policy statement for the account root does this). The stack's API role gets kms:Encrypt and kms:Decrypt for personal access only with a functionpilot:* encryption context, and the other key actions only through DynamoDB;
  • allow logs.<region>.amazonaws.com to use it for the stack's log groups;
  • allow sns.amazonaws.com to use it for the update topic.

The identity that creates or updates the stack also needs kms:Decrypt on the key, because Lambda reads the function code from the encrypted artifacts bucket with the deploying identity's permissions.

The key protects FunctionPilot's own data. Your functions, their environment variables and their logs keep their own encryption.

Stack outputs

OutputUse
PortalUrlThe URL users and admins open. Also the application start URL in your IdP.
ApiUrlHTTP API base URL (informational).
CognitoDomainCognito managed login domain. The OIDC redirect URI is <CognitoDomain>/oauth2/idpresponse.
UserPoolId, UserPoolClientIdCognito identifiers.
SamlAcsUrl, SamlAudienceUriValues for your SAML application.
SpokeTrustedPrincipalThe hub role ARN for the spoke template's TrustedPrincipal.
SpokeTemplateUrlURL of this release's spoke-role.yaml.
LocalAccessRoleArnThe role of the local connection.
UpdateTopicArnSNS topic that announces new releases. Subscribe your own endpoints to it.
TableNameDynamoDB table (retained when the stack is deleted).
ArtifactsBucketNamePrivate bucket with the stack's code. Emptied and deleted with the stack; store nothing else in it.
ProductVersionThe FunctionPilot release the stack runs.

Updating

Each release is a new template; there is no version parameter. To upgrade, update the stack with the new release's template (Update stack › Replace current template) and keep every other parameter value. See the upgrading guide.

To change a setting such as LocalAccessLevel, LocalRegions or SamlMetadataUrl, update the stack with the current template and change only that parameter.

Deleting

  1. Delete the hub stack. The DynamoDB table (<NamePrefix>-table) is retained on purpose, with its settings, grants, saved tests and audit history. Everything else is deleted: the cache table, the Cognito user pool (Cognito-only users are lost), the artifacts bucket, the log groups and the personal access key. A new stack with the same NamePrefix can't be created while the retained table exists.

    To delete the table when you no longer need it, turn off its deletion protection, then delete it.

  2. Delete the spoke stacks or the StackSet in the other accounts.

  3. Delete the SAML or OIDC application in your identity provider.

Troubleshooting

SymptomCheck
Stack creation or update fails at ArtifactInstallThe status reason gives the installer's exit code and log stream (log group /<NamePrefix>/installer). 2: the image has no valid code bundle; contact support. 3: the account has no FunctionPilot licence: subscribe on AWS Marketplace, or deploy in the subscribing account (see Before you start). 4: the upload to the artifacts bucket failed; check SCPs, the S3 endpoint policy and, with KmsKeyArn, the key policy. 5: AWS License Manager stayed unavailable for the whole retry budget; retry the stack operation later. No exit code, with CannotPullContainerError or ResourceInitializationError: the subnets can't reach ECR in us-east-1, S3 or CloudWatch Logs. "The installer task did not finish in time": the same network checks.
Every page says a licence is requiredAdministration › Licence and about shows what License Manager returned. The subscription may have expired, or the stack runs in an account without the licence. Choose Check licence now after renewing.
A connection test fails at AssumeRoleThe spoke's TrustedPrincipal must be the hub's SpokeTrustedPrincipal, the external IDs must match, and the role must be under /functionpilot/. An SCP can also deny sts:AssumeRole, sts:SetSourceIdentity or sts:TagSession.
A Region shows "could not be loaded"The message names the AWS error. Check the Region is enabled in that account, and that no SCP denies Lambda or CloudWatch there.
"Your AWS permissions don't allow this" or an AWS error codeAWS refused the call: the connection's access level, an SCP, or (with personal access) the user's own permissions.
A user gets "too many grants"The user's grants on that function make a session policy larger than AWS allows. Use group grants and broader patterns (for example orders-*) instead of many single names.
ip_not_allowedAdministration › Settings › Network access. The API sees the client's public IP.
A user is refused because their email isn't trustedSee Identity providers: the email must be asserted by SAML, verified by OIDC, or marked verified in Cognito.
SAML error after sign-inThe ACS URL and audience match the outputs; the email attribute is sent; the user is assigned to the app.

Something unclear or missing? Email [email protected].

Tell us what you have. We'll make it better and cheaper.

Send a short description of your current setup. We will tell you what we would improve, what it would cost, and where the savings are. A considered reply from an engineer, not a sales sequence.

TELL US ABOUT YOUR SETUP →