A Config rule that checks that all IAM Users have MFA Enabled.

 
Tags
MFA
Items
4
Size
7.3 KB
AWSTemplateFormatVersion: '2010-09-09'
Resources:
  CustomConfigRule:
    Type: 'AWS::Config::ConfigRule'
    Properties:
      ConfigRuleName: iam_mfa_require
      Description: A Config rule that checks that all IAM Users have MFA Enabled.
      InputParameters: {}
      Scope:
        ComplianceResourceTypes:
          - 'AWS::IAM::User'
      Source:
        Owner: CUSTOM_LAMBDA
        SourceIdentifier:
          'Fn::GetAtt':
            - LambdaFunction
            - Arn
        SourceDetails:
          - EventSource: aws.config
            MessageType: ConfigurationItemChangeNotification
          - EventSource: aws.config
            MessageType: OversizedConfigurationItemChangeNotification
    DependsOn: LambdaInvokePermissions
  LambdaInvokePermissions:
    Type: 'AWS::Lambda::Permission'
    Properties:
      FunctionName:
        'Fn::GetAtt':
          - LambdaFunction
          - Arn
      Action: 'lambda:InvokeFunction'
      Principal: config.amazonaws.com
  LambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      FunctionName: LambdaForiam_mfa_require
      Handler: index.handler
      Role:
        'Fn::GetAtt':
          - LambdaIamRole
          - Arn
      Runtime: nodejs6.10
      Code:
        ZipFile:
          'Fn::Join':
            - |+

            - - ''
              - //
              - >-
                // This file made available under CC0 1.0 Universal
                (https://creativecommons.org/publicdomain/zero/1.0/legalcode)
              - //
              - // Ensure IAM User has MFA Enabled
              - '// Description: Checks that all IAM Users have MFA Enabled'
              - '// '
              - '// Trigger Type: Change Triggered'
              - '// Scope of Changes: IAM:User'
              - '// Required Parameter: None'
              - ''
              - var aws = require('aws-sdk');
              - var config = new aws.ConfigService();
              - var iam = new aws.IAM();
              - ''
              - '// Helper function used to validate input '
              - 'function checkDefined(reference, referenceName) {'
              - '    if (!reference) {'
              - '        console.log("Error: " + referenceName + " is not defined");'
              - '        throw referenceName;'
              - '    }'
              - '    return reference;'
              - '}'
              - ' '
              - >-
                // Check whether the the resource has been deleted. If it has,
                then the evaluation is unnecessary.
              - ' '
              - 'function isApplicable(configurationItem, event){'
              - '    checkDefined(configurationItem, "configurationItem");'
              - '    checkDefined(event, "event");'
              - '    var status = configurationItem.configurationItemStatus;'
              - '    var eventLeftScope = event.eventLeftScope;'
              - '    return (''OK'' === status || ''ResourceDiscovered'' === status) && false === eventLeftScope;'
              - '}'
              - ' '
              - // This is the handler that's invoked by Lambda
              - ''
              - 'exports.handler = function(event, context) {'
              - '    event = checkDefined(event, "event");'
              - '    var invokingEvent = JSON.parse(event.invokingEvent);'
              - '    var ruleParameters = JSON.parse(event.ruleParameters);'
              - '    var configurationItem = checkDefined(invokingEvent.configurationItem, "invokingEvent.configurationItem");'
              - '    var putEvaluationsRequest = {};'
              - '    '
              - '    // Only call out Async if a User'
              - '    if (configurationItem.resourceType === ''AWS::IAM::User'') {'
              - "\t   "
              - '        iam.listMFADevices({ UserName: configurationItem.resourceName }, function(mfaerr, mfadata) {'
              - ''
              - '            var ret = ''NON_COMPLIANT'';'
              - '            '
              - "    \t\tif (!mfaerr) {"
              - "    \t\t\t"
              - "    \t\t    if (mfadata.MFADevices.length > 0) {"
              - '                '
              - '                    ret = ''COMPLIANT'';'
              - '                '
              - '                }'
              - "    \t\t\t"
              - "    \t\t} else {"
              - '    '
              - "    \t\t    console.log(mfaerr);"
              - '    '
              - "    \t\t}"
              - "    \t\t"
              - "    \t\tputEvaluationsRequest.Evaluations = [{"
              - "    \t        ComplianceResourceType: configurationItem.resourceType,"
              - "    \t\t\tComplianceResourceId: configurationItem.resourceId,"
              - "    \t\t\tComplianceType: ret,"
              - "    \t\t\tOrderingTimestamp: configurationItem.configurationItemCaptureTime"
              - "    \t\t}];"
              - "    \t\t    "
              - "    \t    putEvaluationsRequest.ResultToken = event.resultToken;"
              - "    \t\t "
              - '            // Invoke the Config API to report the result of the evaluation'
              - '            config.putEvaluations(putEvaluationsRequest, function (err, data) {'
              - "        \t\t\tif (err) {"
              - "        \t\t\t    context.fail(err);"
              - "        \t\t\t} else {"
              - "        \t\t\t    context.succeed(data);"
              - "        \t\t\t}"
              - '            });'
              - ''
              - "\t    });"
              - "\t   "
              - '    } else {'
              - ' '
              - "\t    // Put together the request that reports the evaluation status"
              - "\t    // Note that we're choosing to report this evaluation against the resource that was passed in."
              - "\t    // You can choose to report this against any other resource type, as long as it is supported by Config rules"
              - "\t    putEvaluationsRequest.Evaluations = [ { ComplianceResourceType: configurationItem.resourceType, ComplianceResourceId: configurationItem.resourceId, ComplianceType: 'NOT_APPLICABLE', OrderingTimestamp: configurationItem.configurationItemCaptureTime } ];"
              - "\t    putEvaluationsRequest.ResultToken = event.resultToken;"
              - "\t "
              - "\t    // Invoke the Config API to report the result of the evaluation"
              - "\t    config.putEvaluations(putEvaluationsRequest, function (err, data) { if (err) { context.fail(err); } else { context.succeed(data); } });"
              - "\t    "
              - '    }'
              - '    '
              - '};'
              - ''
      Timeout: 300
    DependsOn: LambdaIamRole
  LambdaIamRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: IAMRoleForiam_mfa_require
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/IAMReadOnlyAccess'
        - 'arn:aws:iam::aws:policy/service-role/AWSConfigRulesExecutionRole'
        - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
      Policies: []
Parameters: {}
Metadata: {}
Conditions: {}

Customize Cf Template

Rule Parameters

No rule paramters
 
* Required field
Additional Attributes
Lambda Code (Nodejs)