AWSTemplateFormatVersion: '2010-09-09'
Description: 'Remediation of IAM Password Policy'
Resources:
  IAMPasswordLambdaCustom:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt IAMPasswordLambdaFunction.Arn
  IAMPasswordLambdaPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - iam:DeleteAccountPasswordPolicy
            Resource: '*'
  IAMPasswordLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - !Ref 'IAMPasswordLambdaPolicy'
  IAMPasswordLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Description: Reset IAM Password Policy for AWS Account
      Handler: index.lambda_handler
      Runtime: python3.9
      Role: !GetAtt 'IAMPasswordLambdaRole.Arn'
      MemorySize: 128
      Timeout: 10
      Code:
        ZipFile: |
          from __future__ import print_function
          import json
          import boto3
          import urllib3

          SUCCESS = 'SUCCESS'
          FAILED = 'FAILED'
          http = urllib3.PoolManager()

          # cfn-response module
          def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False, reason=None):
              responseUrl = event['ResponseURL']
              print('responseUrl: ' + responseUrl)

              responseBody = {
                  'Status' : responseStatus,
                  'Reason' : reason or "See the details in CloudWatch Log Stream: {}".format(context.log_stream_name),
                  'PhysicalResourceId' : physicalResourceId or context.log_stream_name,
                  'StackId' : event['StackId'],
                  'RequestId' : event['RequestId'],
                  'LogicalResourceId' : event['LogicalResourceId'],
                  'NoEcho' : noEcho,
                  'Data' : responseData
              }

              json_responseBody = json.dumps(responseBody)

              print("Response body:")
              print(json_responseBody)

              headers = {
                  'content-type' : '',
                  'content-length' : str(len(json_responseBody))
              }

              try:
                  response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody)
                  print("Status code:", response.status)
              except Exception as e:
                  print("send(..) failed executing http.request(..):", e)

          # lambda handler for IAM password policy remediator
          def lambda_handler(event, context):
              print('lambda_handler() received event: ' + json.dumps(event))
              result = FAILED

              try:
                  if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
                      response = reset_iam_password_policy()
                      print('iam password policy update completion response: ' + str(response))
                      result = SUCCESS
                  elif event['RequestType'] == 'Delete':
                      print('deletion of CloudFormation stack has no impact on current password policy')
                      result = SUCCESS
              except Exception as e:
                  print('lambda_handler error: ' + str(e))
                  result = FAILED

              send(event, context, result, {})
              return

          # Reset IAM Password Policy for AWS Account according
          #
          def reset_iam_password_policy():
              iam = boto3.client('iam')

              response = iam.delete_account_password_policy()

              return 'reset_account_password_policy() completion response: ' + str(response)