Question: How do I create a config rule for unsupported resource types using AWS Config?

I want to create a config rule for a resource type that isn’t supported by AWS Config. How can I do that?Short descriptionAWS Config allows you to create config rules to verify whether AWS resources comply with a specified configuration. By default, AWS Config supports a limited number of AWS resource types. However, you can also create a custom config rule for resource types that aren’t supported by AWS Config.When a custom config rule is deployed, an AWS Lambda function is created to evaluate the resources. You can create a Lambda function that will evaluate an unsupported resource type and return the compliance result to the custom config rule.

ResolutionNote: The following steps and code example create a custom config role to evaluate Amazon Cognito, which is an unsupported resource.PrerequisitesCreate an AWS Identity and Access Management (IAM) role for the Lambda function to allow Lambda to make the necessary API calls.1.    Open the IAM console, and then choose Roles from the navigation pane.2.    Choose Create role.3.    For Choose a use case, under Common use cases, choose Lambda.4.    Choose Next: Permissions.5.    Choose Create policy.Note: The Create policy page opens in a new tab. You’ll return to the original tab later in this process.6.    Choose the JSON tab, and then enter the following policy:{  "Version": "2012-10-17",  "Statement": [    {      "Sid": "VisualEditor0",      "Effect": "Allow",      "Action": [        "config:PutEvaluations",        "cognito-idp:ListUserPools"      ],      "Resource": "*"    }  ]}7.    Choose Next: Tags, and then choose Next: Review.8.    For Name, enter a policy name.9.    Choose Create policy.10.    Return to the Create role tab from step 5, and then choose the refresh icon on the right side of the policy list.11.    Find and select the new policy name, and then choose Next: Tags.12.    Choose Next: Review.13.    For Role name, enter lambda-role.14.    Choose Create role.Create a Lambda function for the custom config rule1.    Open the Lambda console.2.    Choose Create function.3.    Choose Author from scratch.For Function name, enter a name.For Runtime, select Python 3.8.4.    For Permissions, expand Change default execution role.For Execution role, choose Use an existing role.For Existing role, choose the role that you created to allow Lambda to make the necessary API calls in the Prerequisites section of this article.5.    Choose Create function.6.    For Code source, open the lambda_function.py file in the Lambda console, replace the default code with the following, and then choose Deploy.Note: The following code example finds all the Amazon Cognito UserPools that have MFA: OFF, and marks the results as NON_COMPLIANT. For information and resources to customize this guidance for other unsupported services and objectives, see the article section PutEvaluations API and AWS::::Account resource type value.// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.// SPDX-License-Identifier: MIT-0import jsonimport boto3def lambda_handler(event,context):  cognito = boto3.client('cognito-idp')  userpools = cognito.list_user_pools(    MaxResults=5    )  userpool_list = userpools['UserPools']  userpool_ids = []    for i in userpool_list:    userpool_ids.append(i['Id'])    evaluations = []  orderingtime = json.loads(event['invokingEvent'])['notificationCreationTime']    for j in userpool_ids:     userpool_describe = cognito.describe_user_pool(        UserPoolId = j      )     userpool_MFA = userpool_describe['UserPool']['MfaConfiguration']     if userpool_MFA == 'OFF':       evaluations.append(          {          'ComplianceResourceType': 'AWS::::Account',          'ComplianceResourceId': j,          'ComplianceType': 'NON_COMPLIANT',          'Annotation': 'Test',          'OrderingTimestamp': orderingtime          }        )     else:       evaluations.append(          {          'ComplianceResourceType': 'AWS::::Account',          'ComplianceResourceId': j,          'ComplianceType': 'COMPLIANT',          'Annotation': 'Test',          'OrderingTimestamp': orderingtime          }        )    result_token = event['resultToken']  config = boto3.client('config')  response = config.put_evaluations(              Evaluations = evaluations,              ResultToken = result_token,              TestMode = False            )The preceding code performs three major steps:Lists all resources to create an inventory.Evaluates each resource in the inventory against the specified configuration.Returns the compliance status based on the evaluation using the PutEvaluations API call to AWS Config.Create a custom periodic config ruleYou must now create a custom periodic config rule in AWS Config. Then, associate the custom rule with the Lambda function that you created in the previous section. For instructions, see Creating a custom rule.Important: Note the ARN of the new custom config rule for a later step.Authorize the AWS Config service to invoke the Lambda function1.    Open the Lambda console, and then choose Functions  from the navigation pane.2.    Choose the hyperlinked Function name for your Lambda function to open the function details.3.    From the Configuration tab, choose Permissions.4.    For Resource-based policy, choose Add permissions.For Policy statement, choose AWS service.For Service, choose Other.For Principal, enter config.amazonaws.com.For Source ARN, enter the ARN of the custom config rule that you created in the previous section. Note: To find the custom config rule ARN, open the AWS Config console, and then choose Rules from the navigation pane. Choose the hyperlinked rule Name, and then copy the Config rule ARN.For Action, choose lambda:InvokeFunction.For Statement ID, enter a unique ID.5.    Choose Save.PutEvaluations API and AWS::::Account resource type valueThe steps and examples in this article find all the Amazon Cognito UserPools that have MFA: OFF, and mark the results as NON_COMPLIANT. However, you can modify the process to meet your specific requirements with any unsupported resource type. You can find more code examples that use the AWS::::Account resource type on the aws-config-rules GitHub page.The PutEvaluations API call includes the following parameters:{   "Evaluations": [     {      "Annotation": "string",     "ComplianceResourceId": "string",     "ComplianceResourceType": "string",     "ComplianceType": "string",     "OrderingTimestamp": number    }   ],   "ResultToken": "string",   "TestMode": boolean}For unsupported resource types, you must enter AWS::::Account as the value for ComplianceResourceType. The AWS::::Account resource type reports on the whole account instead of a specific resource.Example:{  'ComplianceResourceType': 'AWS::::Account',  'ComplianceResourceId': 'Resource Id',  'ComplianceType': 'COMPLIANT'|'NON_COMPLIANT',  'Annotation': 'string',  'OrderingTimestamp': number}If an unsupported resource is entered as the ComplianceResourceType value instead of AWS::::Account, then you get the following errors:On the console: "No results available"Lambda CloudWatch logs: ""Error: ClientError: An error occurred (InternalFailure) when calling the PutEvaluations operation (reached max retries: 4)"Cloudtrail (PutEvaluations API): "Error: ClientError: An error occurred (InternalFailure) when calling the PutEvaluations operation (reached max retries: 4)Note the following when you use the AWS::::Account resource type value:1.    A configuration item isn’t recorded for this resource type.2.    A compliance configuration item under AWS::Config::ResourceCompliance isn’t recorded for this resource type.3.    A resource timeline isn’t available for this resource type.4.    In the AWS Management Console, the Manage Resource button is unavailable for this resource type.

Related informationCreating an AWS Lambda function for a custom config rule