AWS
Connect your AWS accounts to OpsLyft optimization.
Contents
- Introduction
- Values
- Choose your path
- Integration Steps
- Set up your terminal
- Share with us
- Reference commands
- Troubleshooting
- Remove access
Introduction
OpsLyft reads your AWS accounts with a read-only IAM role that you create in each account.
OpsLyft assumes the role on a schedule and reads configuration, tags and sizing.
Values
| Name | Value |
|---|---|
| OPSLYFT_PRINCIPAL_ARN | arn:aws:iam::612488371952:root |
| TEMPLATE_URL | https://aws-optimization-integration-permissions-template.s3.amazonaws.com/customer-onboarding-polling.yaml |
| OPSLYFT_ROLE | OpsLyftOptimizationRole |
| STACK_NAME | opslyft-optimization-onboarding |
| STACKSET_NAME | opslyft-optimization-onboarding-set |
Choose your path
OpsLyft needs the role to be attached in every account you want to see optimization data for. Three options (or a combination of them) for integration:
- Whole organization with a StackSet (preferred): deploys the role to every current and future account in your organization except the management account. Service-managed StackSets do not deploy to the management account, so deploy a separate stack there.
- Specific OUs: as option 1, with your OU IDs instead of the root ID.
- Individual accounts: deploy one stack in each account.
Integration Steps
Whole organization (or some OUs) - preferred
-
Sign in to your management account.
-
Run:
export STACKSET_NAME=opslyft-optimization-onboarding-set export ROOT_ORG_ID=$(aws organizations list-roots --query 'Roots[0].Id' --output text)For specific OUs, set
ROOT_ORG_IDto your OU IDs, separated by commas. -
Create the StackSet and deploy it.
aws cloudformation create-stack-set --stack-set-name "$STACKSET_NAME" --template-url "$TEMPLATE_URL" --region "$REGION" \
--permission-model SERVICE_MANAGED --auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false \
--capabilities CAPABILITY_NAMED_IAM --parameters ParameterKey=ExternalId,ParameterValue="$EXTERNAL_ID" \
ParameterKey=RoleName,ParameterValue="$ROLE_NAME" ParameterKey=OpsLyftAssumingPrincipalArn,ParameterValue="$OPSLYFT_PRINCIPAL_ARN"
aws cloudformation create-stack-instances --stack-set-name "$STACKSET_NAME" --deployment-targets OrganizationalUnitIds="$ROOT_ORG_ID" \
--regions "$REGION" --operation-preferences FailureTolerancePercentage=100,MaxConcurrentPercentage=100 --region "$REGION"Check (after the operation finishes): run Check the StackSet operation status; it prints SUCCEEDED. Then run List the StackSet instances; every account shows CURRENT.
An immediate permissions error means trusted access is off (Check that StackSets trusted access is on) or you are not in the management account (or a delegated administrator). For accounts FAILED, check they are not suspended, then re-run create-stack-instances.
- In the same account, deploy the management account's own stack.
- Run the stack deployment
aws cloudformation create-stack --stack-name "$STACK_NAME" --template-url "$TEMPLATE_URL" --region "$REGION" \
--capabilities CAPABILITY_NAMED_IAM --parameters ParameterKey=ManagementAccount,ParameterValue=yes ParameterKey=ExternalId,ParameterValue="$EXTERNAL_ID" \
ParameterKey=RoleName,ParameterValue="$ROLE_NAME" ParameterKey=OpsLyftAssumingPrincipalArn,ParameterValue="$OPSLYFT_PRINCIPAL_ARN"
aws cloudformation wait stack-create-complete --stack-name "$STACK_NAME" --region "$REGION"Check: run Check the stack status; it prints CREATE_COMPLETE. If not, see Troubleshooting.
- Check the role in the management account
Run Show the role trust and policy.
Check: the trust names OpsLyft's account with an sts:ExternalId condition equal to $EXTERNAL_ID, and the policy is <ROLE-NAME>Policy. If not, update the stack or StackSet (see Update the role later).
Then go to Share with us.
Individual accounts (repeat in each account)
- Sign in to the account you want to integrate.
- Run Set up your terminal again in this account.
- Check that the role name is free
- Check that the stack name is free
- Deploy the stack. Creates the role and its policy.
aws cloudformation create-stack --stack-name "$STACK_NAME" --template-url "$TEMPLATE_URL" --region "$REGION" \
--capabilities CAPABILITY_NAMED_IAM --parameters ParameterKey=ExternalId,ParameterValue="$EXTERNAL_ID" \
ParameterKey=RoleName,ParameterValue="$ROLE_NAME" ParameterKey=OpsLyftAssumingPrincipalArn,ParameterValue="$OPSLYFT_PRINCIPAL_ARN"
aws cloudformation wait stack-create-complete --stack-name "$STACK_NAME" --region "$REGION"Check: run Check the stack status; it prints CREATE_COMPLETE. If not, run Show the recent stack events to see why (see Troubleshooting).
Check the role. Run Show the role trust and policy.
Check: the trust names OpsLyft's account with an sts:ExternalId condition equal to $EXTERNAL_ID, and the policy is <ROLE-NAME>Policy. If not, update the stack (see Update the role later).
Then go to Share with us.
Update the role later
Use this for an updated template file or a changed ExternalId (tell us the new value). Update the stack with the same parameters; only the role's managed policy is replaced. For a StackSet use aws cloudformation update-stack-set with the same template and parameters.
aws cloudformation update-stack --stack-name "$STACK_NAME" --template-url "$TEMPLATE_URL" --region "$REGION" --capabilities CAPABILITY_NAMED_IAM \
--parameters ParameterKey=ExternalId,ParameterValue="$EXTERNAL_ID" ParameterKey=ManagementAccount,UsePreviousValue=true \
ParameterKey=RoleName,UsePreviousValue=true ParameterKey=OpsLyftAssumingPrincipalArn,UsePreviousValue=trueCheck: run Check the stack status; it prints UPDATE_COMPLETE.
Adding or changing accounts:
- Account inside your organization: nothing to do with a StackSet with automatic deployment and an organization we can list; the StackSet deploys the role to new accounts and OpsLyft adds them automatically.
- Account outside it, or an organization not connected for listing: deploy the stack there (Individual accounts), then send OpsLyft the updated values with the account added.
- Changing regions or the role: send OpsLyft the updated values (with the new role ARN).
Set up your terminal
Prerequisite checks:
- In your own terminal only (not needed in AWS CloudShell): Check that the AWS CLI is signed in
- Check your deploy rights
- Generate the ExternalId once and keep it; reuse it in every deployment. Run the first command only in your first deployment.
export EXTERNAL_ID=$(uuidgen | tr 'A-Z' 'a-z')
echo "$EXTERNAL_ID"- Then run variable exports
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export REGION=${AWS_REGION:-${AWS_DEFAULT_REGION:-$(aws configure get region)}}
export OPSLYFT_PRINCIPAL_ARN=arn:aws:iam::612488371952:root
export TEMPLATE_URL=https://aws-optimization-integration-permissions-template.s3.amazonaws.com/customer-onboarding-polling.yaml
export ROLE_NAME=OpsLyftOptimizationRole
export STACK_NAME=opslyft-optimization-onboardingShare with us
Send OpsLyft these values. OpsLyft assumes your role, so the values include its ARN.
| Field | What to send | How to get it |
|---|---|---|
| Organization ID | Your organization ID. Leave empty if you have no organization or do not want it listed. | Check the organization and management account IDs (first value) |
| Management account ID | The account where you deployed with ManagementAccount set to yes. Without it we cannot list your organization's accounts and you list them yourself. | Check the organization and management account IDs (second value) |
| Organizational units | Optional. OU IDs (ou-...) if you want only some OUs. Empty means the whole organization. | Enter manually |
| Account IDs | The 12-digit IDs of accounts outside an organization, or ones you want to list yourself. | echo "$ACCOUNT_ID" in that account |
| Accounts to exclude | Accounts OpsLyft must never read, even if they are in your organization or have the role. | Enter manually |
| Regions | Leave empty to cover all regions enabled in each account (needs ec2:DescribeRegions), or list the regions, optionally with regions to leave out. | Enter manually |
| ExternalId | The UUID you generated. Say so if the management account's value, or any account's, differs. | echo "$EXTERNAL_ID" |
| Role ARN | The role's ARN in each account. If every account uses the same role name, say so and send one ARN with the account IDs. | Show the role ARN in each account you send |
| How you deployed | StackSet, stacks, script or console, with the StackSet or stack IDs if you have them. For reference only. | Show the StackSet ID (StackSet only), Show the stack ID |
After you send it: OpsLyft reads your accounts from the next scheduled read. A new account first shows as waiting for access, then becomes active by itself once OpsLyft can use the role.
Reference commands
Shows the AWS CLI version (v2 is required) and the account you are signed in to.
aws --version
aws sts get-caller-identity --query '[Account,Arn]' --output textPrints aws-cli/2..., then your account ID and identity ARN.
Checks that you can create IAM roles, policies and CloudFormation stacks.
IDENTITY_ARN=$(aws sts get-caller-identity --query Arn --output text | sed -E '/:assumed-role\//{s#:sts:#:iam:#; s#assumed-role/#role/#; s#/[^/]+$##;}')
aws iam simulate-principal-policy --policy-source-arn "$IDENTITY_ARN" --action-names iam:CreateRole iam:CreatePolicy cloudformation:CreateStack \
--query 'EvaluationResults[].[EvalActionName,EvalDecision]' --output textPrints three lines, each must be allowed (for an assumed role use the role's ARN as the policy source).
Checks that the integration template can be read.
aws cloudformation validate-template --template-url "$TEMPLATE_URL" --query 'Parameters[].ParameterKey' --output textShould list ExternalId, OpsLyftAssumingPrincipalArn, RoleName and ManagementAccount.
Checks that no IAM role with that name exists.
aws iam get-role --role-name "$ROLE_NAME"Fails with NoSuchEntity if the name is free; otherwise the role already exists.
Checks that no stack with that name exists.
aws cloudformation describe-stacks --stack-name "$STACK_NAME" --region "$REGION"Returns ValidationError ... Stack with id <name> does not exist if the name is free; otherwise the stack already exists.
Checks that no StackSet with that name exists.
aws cloudformation describe-stack-set --stack-set-name "$STACKSET_NAME" --region "$REGION"Returns StackSetNotFoundException ... not found if the name is free; otherwise the StackSet already exists.
Checks that trusted access for StackSets is on. Run it in the management account, for organization setups.
aws organizations list-aws-service-access-for-organization --output text \
--query "EnabledServicePrincipals[?ServicePrincipal=='member.org.stacksets.cloudformation.amazonaws.com'].ServicePrincipal"Prints the service principal. If it prints nothing, enable trusted access in CloudFormation > StackSets.
Shows the status of the latest StackSet operation.
aws cloudformation list-stack-set-operations --stack-set-name "$STACKSET_NAME" --query 'Summaries[0].Status' --output textPrints the status of the latest operation; SUCCEEDED when done.
Lists each StackSet account and its status.
aws cloudformation list-stack-instances --stack-set-name "$STACKSET_NAME" --query 'Summaries[].[Account,Status]' --output textPrints each account and its status; every account should be CURRENT. An account that is not CURRENT has failed.
Shows the status of the stack.
aws cloudformation describe-stacks --stack-name "$STACK_NAME" --region "$REGION" --query 'Stacks[0].StackStatus' --output textPrints the status: CREATE_COMPLETE after a deploy, UPDATE_COMPLETE after an update.
Shows the role's trust document and attached policy.
aws iam get-role --role-name "$ROLE_NAME" --query 'Role.AssumeRolePolicyDocument'
aws iam list-attached-role-policies --role-name "$ROLE_NAME"Prints the role's trust document and its attached policy. Take the sts:ExternalId condition from the trust and the policy name from the list.
Shows the latest stack events. Run it when a stack deploy fails.
aws cloudformation describe-stack-events --stack-name "$STACK_NAME" --region "$REGION" --max-items 10Prints the latest events, including the reason a stack failed.
Shows your organization ID and management account ID. Run it in the management account, for organization setups.
aws organizations describe-organization --query 'Organization.[Id,MasterAccountId]' --output textPrints the organization ID, then the management account ID.
Shows the role ARN. Run it in each account you send.
aws iam get-role --role-name "$ROLE_NAME" --query Role.Arn --output textPrints the role's ARN.
Shows the StackSet ID. Run it for StackSet setups only.
aws cloudformation describe-stack-set --stack-set-name "$STACKSET_NAME" --query 'StackSet.StackSetId' --output textPrints the StackSet ID.
Shows the stack ID. Run it in each account where you deployed a stack.
aws cloudformation describe-stacks --stack-name "$STACK_NAME" --region "$REGION" --query 'Stacks[0].StackId' --output textPrints the stack ID.
Troubleshooting
OpsLyft shows what it finds, with a reason. If access fails, OpsLyft keeps the last data it read and shows the reason. OpsLyft changes nothing in your accounts. Fix access, then wait for the next scheduled read.
| What you see | Cause and fix |
|---|---|
Stack or StackSet refused with a validation error on ExternalId | ExternalId is missing or does not match the allowed format. Pass ParameterKey=ExternalId,ParameterValue="$EXTERNAL_ID" with your UUID (step 3 of Set up your terminal). |
Stack fails with Insufficient permissions to create IAM resources | Tick the IAM acknowledgment and check you can create IAM roles and policies (Check your deploy rights). |
| Account "waiting for access" | No role yet, a role ARN or ExternalId mismatch, or the role cannot be assumed (AWS gives the same answer for all three). Check the role exists with the submitted ARN and that the stack's ExternalId equals the submitted one (a trailing space breaks it). OpsLyft retries on its own. |
| Account not activated, "account ID mismatch" | The role belongs to a different account than the ID submitted. Check the ID (step 1 of Set up your terminal). |
| Account "missing" | It left your organization or access failed several reads in a row. Its last known resources stay visible; it returns to active by itself when access works again. |
| Account shown as excluded; resource type "not permitted" | The account is on your exclusion list, or that type cannot be read in the account (other types are still read). |
StackSet fails at once, or FAILED for some accounts | Trusted access is off (Check that StackSets trusted access is on), you are not in the management account (or a delegated administrator), an account is suspended, or more than one region was used. Re-run create-stack-instances. |
| OpsLyft lists no accounts from your organization | Check ManagementAccount is yes in the management account's stack and the submitted management account ID is that account's. |
Remove access
To stop OpsLyft reading an account, add it to "Accounts to exclude" in the values you send OpsLyft (OpsLyft does not read it again until you remove it from the list).
To remove access fully, also delete the stack in that account:
aws cloudformation delete-stack --stack-name "$STACK_NAME" --region "$REGION"For a StackSet, first delete its instances (use the same ROOT_ORG_ID value as in the StackSet steps). This removes the role from every account it covers:
aws cloudformation delete-stack-instances --stack-set-name "$STACKSET_NAME" --deployment-targets OrganizationalUnitIds="$ROOT_ORG_ID" --regions "$REGION" --no-retain-stacks --region "$REGION"Run Check the StackSet operation status until it prints SUCCEEDED, then run List the StackSet instances: it should print nothing. Then delete the StackSet:
aws cloudformation delete-stack-set --stack-set-name "$STACKSET_NAME" --region "$REGION"