Skip to content

Commit

Permalink
Release: 1.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AWS committed Jul 21, 2022
1 parent 3169235 commit 0f2caea
Show file tree
Hide file tree
Showing 17 changed files with 269 additions and 6 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ for more information.

Now that you have configured and deployed AWS Control Tower Account Factory for Terraform, follow the steps outlined in [Post-deployment steps](https://docs.aws.amazon.com/controltower/latest/userguide/aft-post-deployment.html) and [Provision accounts with AWS Control Tower Account Factory for Terraform](https://docs.aws.amazon.com/controltower/latest/userguide/taf-account-provisioning.html) to begin using your environment.

## Collection of Operational Metrics
As of version 1.6.0, AFT collects anonymous operational metrics to help AWS improve the quality and features of the solution. For more information, including how to disable this capability, please see the [documentation here](https://docs.aws.amazon.com/controltower/latest/userguide/aft-operational-metrics.html).


<!-- BEGIN_TF_DOCS -->
## Requirements
Expand Down Expand Up @@ -108,6 +111,7 @@ Now that you have configured and deployed AWS Control Tower Account Factory for
| <a name="input_aft_framework_repo_git_ref"></a> [aft\_framework\_repo\_git\_ref](#input\_aft\_framework\_repo\_git\_ref) | Git branch from which the AFT framework should be sourced from | `string` | `null` | no |
| <a name="input_aft_framework_repo_url"></a> [aft\_framework\_repo\_url](#input\_aft\_framework\_repo\_url) | Git repo URL where the AFT framework should be sourced from | `string` | `"https://github.com/aws-ia/terraform-aws-control_tower_account_factory.git"` | no |
| <a name="input_aft_management_account_id"></a> [aft\_management\_account\_id](#input\_aft\_management\_account\_id) | AFT Management Account ID | `string` | n/a | yes |
| <a name="input_aft_metrics_reporting"></a> [aft\_metrics\_reporting](#input\_aft\_metrics\_reporting) | Flag toggling reporting of operational metrics | `bool` | `true` | no |
| <a name="input_aft_vpc_cidr"></a> [aft\_vpc\_cidr](#input\_aft\_vpc\_cidr) | CIDR Block to allocate to the AFT VPC | `string` | `"192.168.0.0/22"` | no |
| <a name="input_aft_vpc_endpoints"></a> [aft\_vpc\_endpoints](#input\_aft\_vpc\_endpoints) | Flag turning VPC endpoints on/off for AFT VPC | `bool` | `true` | no |
| <a name="input_aft_vpc_private_subnet_01_cidr"></a> [aft\_vpc\_private\_subnet\_01\_cidr](#input\_aft\_vpc\_private\_subnet\_01\_cidr) | CIDR Block to allocate to the Private Subnet 01 | `string` | `"192.168.0.0/24"` | no |
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.5.2
1.6.0
1 change: 1 addition & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,5 @@ module "aft_ssm_parameters" {
account_provisioning_customizations_repo_branch = var.account_provisioning_customizations_repo_branch
maximum_concurrent_customizations = var.maximum_concurrent_customizations
github_enterprise_url = var.github_enterprise_url
aft_metrics_reporting = var.aft_metrics_reporting
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ phases:
fi
build:
on-failure: ABORT
on-failure: CONTINUE
commands:
# Apply Customizations
- |
Expand Down Expand Up @@ -136,6 +136,14 @@ phases:
post_build:
on-failure: ABORT
commands:
- export PYTHONPATH="$DEFAULT_PATH/aws-aft-core-framework/sources/aft-lambda-layer:$PYTHONPATH"
- export AWS_PROFILE=aft-management
- python3 $DEFAULT_PATH/aws-aft-core-framework/sources/aft-lambda-layer/aft_common/report_metrics.py --codebuild-name "aft-account-customizations" --codebuild-status $CODEBUILD_BUILD_SUCCEEDING
- unset AWS_PROFILE
- |
if [[ $CODEBUILD_BUILD_SUCCEEDING == 0 ]]; then
exit 1
fi
- |
if [[ ! -z "$CUSTOMIZATION" ]]; then
source $DEFAULT_PATH/api-helpers-venv/bin/activate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ phases:
- unset AWS_PROFILE

build:
on-failure: ABORT
on-failure: CONTINUE
commands:
# Apply customizations
- source $DEFAULT_PATH/aft-venv/bin/activate
Expand Down Expand Up @@ -115,6 +115,14 @@ phases:
post_build:
on-failure: ABORT
commands:
- export PYTHONPATH="$DEFAULT_PATH/aws-aft-core-framework/sources/aft-lambda-layer:$PYTHONPATH"
- export AWS_PROFILE=aft-management
- python3 $DEFAULT_PATH/aws-aft-core-framework/sources/aft-lambda-layer/aft_common/report_metrics.py --codebuild-name "aft-global-customizations" --codebuild-status $CODEBUILD_BUILD_SUCCEEDING
- unset AWS_PROFILE
- |
if [[ $CODEBUILD_BUILD_SUCCEEDING == 0 ]]; then
exit 1
fi
- source $DEFAULT_PATH/api-helpers-venv/bin/activate
- export AWS_PROFILE=aft-target
- $DEFAULT_PATH/api_helpers/post-api-helpers.sh
15 changes: 15 additions & 0 deletions modules/aft-ssm-parameters/ssm.tf
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,18 @@ resource "aws_ssm_parameter" "aft_maximum_concurrent_customizations" {
value = var.maximum_concurrent_customizations
type = "String"
}

resource "aws_ssm_parameter" "aft_metrics_reporting" {
name = "/aft/config/metrics-reporting"
value = var.aft_metrics_reporting
type = "String"
}

resource "random_uuid" "metrics_reporting_uuid" {
}

resource "aws_ssm_parameter" "aft_metrics_reporting_uuid" {
name = "/aft/config/metrics-reporting-uuid"
value = random_uuid.metrics_reporting_uuid.result
type = "String"
}
4 changes: 4 additions & 0 deletions modules/aft-ssm-parameters/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,7 @@ variable "maximum_concurrent_customizations" {
variable "aft_version" {
type = string
}

variable "aft_metrics_reporting" {
type = string
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

import aft_common.aft_utils as utils
import jsonschema
from aft_common.aft_types import AftAccountInfo
from aft_common.auth import AuthClient
from aft_common.types import AftAccountInfo
from boto3.session import Session
from botocore.exceptions import ClientError

Expand Down
8 changes: 7 additions & 1 deletion sources/aft-lambda-layer/aft_common/aft_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
STSClient = object


from aft_common.types import AftAccountInfo
from aft_common.aft_types import AftAccountInfo

from .logger import Logger

Expand Down Expand Up @@ -129,6 +129,12 @@
SSM_PARAM_ACCOUNT_LOG_ARCHIVE_ACCOUNT_ID = "/aft/account/log-archive/account-id"
SSM_PARAM_ACCOUNT_AFT_MANAGEMENT_ACCOUNT_ID = "/aft/account/aft-management/account-id"

SSM_PARAM_ACCOUNT_AFT_VERSION = "/aft/config/aft/version"
SSM_PARAM_ACCOUNT_TERRAFORM_VERSION = "/aft/config/terraform/version"

SSM_PARAM_AFT_METRICS_REPORTING = "/aft/config/metrics-reporting"
SSM_PARAM_AFT_METRICS_REPORTING_UUID = "/aft/config/metrics-reporting-uuid"


# INIT
def get_logger() -> Logger:
Expand Down
136 changes: 136 additions & 0 deletions sources/aft-lambda-layer/aft_common/metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

import json
from datetime import datetime
from typing import Any, Dict, Optional, TypedDict

import requests # type: ignore
from aft_common import aft_utils as utils
from aft_common.auth import AuthClient
from boto3.session import Session


class MetricsPayloadType(TypedDict):
Solution: str
TimeStamp: str
Version: Optional[str]
UUID: Optional[str]
Data: Dict[str, Any]


class AFTMetrics:
def __init__(self) -> None:

self.solution_id = "SO0089-aft"
self.api_endpoint = "https://metrics.awssolutionsbuilder.com/generic"
self.auth = AuthClient()

def _get_uuid(self, aft_management_session: Session) -> str:

uuid = utils.get_ssm_parameter_value(
aft_management_session, utils.SSM_PARAM_AFT_METRICS_REPORTING_UUID
)
return uuid

def _metrics_reporting_enabled(self, aft_management_session: Session) -> bool:

flag = utils.get_ssm_parameter_value(
aft_management_session, utils.SSM_PARAM_AFT_METRICS_REPORTING
)

if flag.lower() == "true":
return True
return False

def _get_aft_deployment_config(
self, aft_management_session: Session
) -> Dict[str, str]:

config = {}

config["cloud_trail_enabled"] = utils.get_ssm_parameter_value(
aft_management_session,
utils.SSM_PARAM_FEATURE_CLOUDTRAIL_DATA_EVENTS_ENABLED,
)
config["enterprise_support_enabled"] = utils.get_ssm_parameter_value(
aft_management_session, utils.SSM_PARAM_FEATURE_ENTERPRISE_SUPPORT_ENABLED
)
config["delete_default_vpc_enabled"] = utils.get_ssm_parameter_value(
aft_management_session, utils.SSM_PARAM_FEATURE_DEFAULT_VPCS_ENABLED
)

config["aft_version"] = utils.get_ssm_parameter_value(
aft_management_session, utils.SSM_PARAM_ACCOUNT_AFT_VERSION
)

config["terraform_version"] = utils.get_ssm_parameter_value(
aft_management_session, utils.SSM_PARAM_ACCOUNT_TERRAFORM_VERSION
)

config["region"] = utils.get_session_info(aft_management_session)["region"]

return config

def wrap_event_for_api(
self, aft_management_session: Session, event: Dict[str, Any]
) -> MetricsPayloadType:

payload: MetricsPayloadType = {
"Solution": self.solution_id,
"TimeStamp": datetime.utcnow().isoformat(timespec="seconds"),
"Version": None,
"UUID": None,
"Data": {},
}
payload["Solution"] = self.solution_id

data_body: Dict[str, Any] = {}
data_body["event"] = event

errors = []

try:
payload["Version"] = utils.get_ssm_parameter_value(
aft_management_session, utils.SSM_PARAM_ACCOUNT_AFT_VERSION
)
except Exception as e:
payload["Version"] = None
errors.append(str(e))

try:
payload["UUID"] = self._get_uuid(aft_management_session)
except Exception as e:
payload["UUID"] = None
errors.append(str(e))

try:
data_body["config"] = self._get_aft_deployment_config(
aft_management_session
)
except Exception as e:
data_body["config"] = None
errors.append(str(e))

if not errors:
data_body["error"] = None
else:
data_body["error"] = " | ".join(errors)

payload["Data"] = data_body

return payload

def post_event(self, action: str, status: Optional[str] = None) -> None:

aft_management_session = self.auth.get_aft_management_session()

if self._metrics_reporting_enabled(aft_management_session):

event = {"action": action, "status": status}

payload = self.wrap_event_for_api(aft_management_session, event)

response = requests.post(self.api_endpoint, json=payload)

return None
38 changes: 38 additions & 0 deletions sources/aft-lambda-layer/aft_common/report_metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

import argparse

from aft_common import aft_utils as utils
from aft_common.metrics import AFTMetrics

logger = utils.get_logger()

if __name__ == "__main__":

parser = argparse.ArgumentParser(
description="Script called from within CodeBuild containers to report back AFT usage metrics"
)

parser.add_argument(
"--codebuild-name", type=str, help="Name of the build container"
)
parser.add_argument(
"--codebuild-status",
type=int,
help="Whether the build succeeded or not (1 or 0)",
)

args = parser.parse_args()

codebuild_name = args.codebuild_name
codebuild_status = "SUCCEEDED" if args.codebuild_status == 1 else "FAILED"

try:
aft_metrics = AFTMetrics()
aft_metrics.post_event(action=args.codebuild_name, status=args.codebuild_status)
logger.info(f"Successfully logged metrics. Action: {args.codebuild_name}")
except Exception as e:
logger.info(
f"Unable to report metrics. Action: {args.codebuild_name}; Error: {e}"
)
1 change: 1 addition & 0 deletions sources/aft-lambda-layer/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"whaaaaat == 0.5.2",
"Jinja2 == 3.0.3",
"jsonschema == 4.3.2",
"requests == 2.28.0",
],
extras_require={
"dev": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from aft_common import aft_utils as utils
from aft_common import notifications
from aft_common.account_provisioning_framework import ProvisionRoles, get_account_info
from aft_common.aft_types import AftAccountInfo
from aft_common.auth import AuthClient
from aft_common.types import AftAccountInfo
from boto3.session import Session

if TYPE_CHECKING:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
)
from aft_common.auth import AuthClient
from aft_common.exceptions import NoAccountFactoryPortfolioFound
from aft_common.metrics import AFTMetrics
from boto3.session import Session

if TYPE_CHECKING:
Expand All @@ -31,6 +32,7 @@
def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None:
aft_management_session = Session()
auth = AuthClient()

try:
account_request = AccountRequest(auth=auth)
try:
Expand All @@ -55,6 +57,9 @@ def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None:
),
)
if sqs_message is not None:

aft_metrics = AFTMetrics()

sqs_body = json.loads(sqs_message["Body"])
ct_request_is_valid = True
if sqs_body["operation"] == "ADD":
Expand All @@ -68,6 +73,17 @@ def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None:
request=sqs_body,
)

action = "new-account-creation-invoked"
try:
aft_metrics.post_event(action=action, status="SUCCEEDED")
logger.info(
f"Successfully logged metrics. Action: {action}"
)
except Exception as e:
logger.info(
f"Unable to report metrics. Action: {action}; Error: {e}"
)

elif sqs_body["operation"] == "UPDATE":
ct_request_is_valid = modify_ct_request_is_valid(sqs_body)
if ct_request_is_valid:
Expand All @@ -76,6 +92,17 @@ def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None:
ct_management_session=ct_management_session,
request=sqs_body,
)

action = "existing-account-update-invoked"
try:
aft_metrics.post_event(action=action, status="SUCCEEDED")
logger.info(
f"Successfully logged metrics. Action: {action}"
)
except Exception as e:
logger.info(
f"Unable to report metrics. Action: {action}; Error: {e}"
)
else:
logger.info("Unknown operation received in message")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> None:
session = Session()
auth = AuthClient()

try:
ct_management_session = auth.get_ct_management_session(
role_name=ProvisionRoles.SERVICE_ROLE_NAME
Expand Down
Loading

0 comments on commit 0f2caea

Please sign in to comment.