diff --git a/docs/rules.md b/docs/rules.md
index 3099e5a0aa..b1eff7a6e0 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -46,7 +46,7 @@ To include these rules, use the `-e/include-experimental` argument when running
## Rules
(_This documentation is generated by running `cfn-lint --update-documentation`, do not alter this manually_)
-The following **152** rules are applied by this linter:
+The following **153** rules are applied by this linter:
| Rule ID | Title | Description | Config
(Name:Type:Default) | Source | Tags |
| -------- | ----- | ----------- | ---------- | ------ | ---- |
@@ -191,6 +191,7 @@ The following **152** rules are applied by this linter:
| [W2510](../src/cfnlint/rules/parameters/LambdaMemorySize.py) | Parameter Memory Size attributes should have max and min | Check if a parameter that is used for Lambda memory size should have a min and max size that matches Lambda constraints | | [Source](https://docs.aws.amazon.com/lambda/latest/dg/API_CreateFunction.html#SSS-CreateFunction-request-MemorySize) | `parameters`,`lambda` |
| [W2511](../src/cfnlint/rules/resources/iam/PolicyVersion.py) | Check IAM Resource Policies syntax | See if the elements inside an IAM Resource policy are configured correctly. | | [Source](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html) | `properties`,`iam` |
| [W2531](../src/cfnlint/rules/resources/lmbd/DeprecatedRuntimeEol.py) | Check if EOL Lambda Function Runtimes are used | Check if an EOL Lambda Runtime is specified and give a warning if used. | | [Source](https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html) | `resources`,`lambda`,`runtime` |
+| [W2533](../src/cfnlint/rules/resources/lmbd/ZipPackageRequiredProperties.py) | Check required properties for Lambda if the deployment package is a .zip file | When the package type is Zip, you must also specify the `handler` and `runtime` properties. | | [Source](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html) | `resources`,`lambda` |
| [W3002](../src/cfnlint/rules/resources/properties/PropertiesTemplated.py) | Warn when properties are configured to only work with the package command | Some properties can be configured to only work with the CloudFormationpackage command. Warn when this is the case so user is aware. | | [Source](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/package.html) | `resources` |
| [W3005](../src/cfnlint/rules/resources/DependsOnObsolete.py) | Check obsolete DependsOn configuration for Resources | Check if DependsOn is specified if not needed. A Ref or a Fn::GetAtt already is an implicit dependency. | | [Source](https://aws.amazon.com/blogs/devops/optimize-aws-cloudformation-templates/) | `resources`,`dependson`,`ref`,`getatt` |
| [W3010](../src/cfnlint/rules/resources/properties/AvailabilityZone.py) | Availability Zone Parameters should not be hardcoded | Check if an Availability Zone property is hardcoded. | | [Source](https://github.com/aws-cloudformation/cfn-python-lint) | `parameters`,`availabilityzone` |
diff --git a/src/cfnlint/rules/resources/lmbd/ZipPackageRequiredProperties.py b/src/cfnlint/rules/resources/lmbd/ZipPackageRequiredProperties.py
new file mode 100644
index 0000000000..6254b82f28
--- /dev/null
+++ b/src/cfnlint/rules/resources/lmbd/ZipPackageRequiredProperties.py
@@ -0,0 +1,76 @@
+"""
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: MIT-0
+"""
+from cfnlint.rules import CloudFormationLintRule, RuleMatch
+
+
+class ZipPackageRequiredProperties(CloudFormationLintRule):
+ id = "W2533"
+ shortdesc = (
+ "Check required properties for Lambda if the deployment package is a .zip file"
+ )
+ description = (
+ "When the package type is Zip, "
+ "you must also specify the `handler` and `runtime` properties."
+ )
+ source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html"
+ tags = ["resources", "lambda"]
+
+ def match(self, cfn):
+ matches = []
+ required_properties = [
+ "Handler",
+ "Runtime",
+ ] # required if package is a .zip file
+
+ resources = cfn.get_resources(["AWS::Lambda::Function"])
+
+ for resource_name, resource in resources.items():
+ properties = resource.get("Properties")
+ if not isinstance(properties, dict):
+ continue
+
+ for scenario in cfn.get_object_without_conditions(
+ properties, ["PackageType", "Code", "Handler", "Runtime"]
+ ):
+ props = scenario.get("Object")
+ path = ["Resources", resource_name, "Properties"]
+
+ # check is zip deployment
+ is_zip_deployment = True
+ code = props.get("Code")
+
+ if props.get("PackageType") == "Zip":
+ path.append("PackageType")
+ elif isinstance(code, dict) and (
+ code.get("ZipFile") or code.get("S3Key")
+ ):
+ path.append("Code")
+ else:
+ is_zip_deployment = False
+
+ if not is_zip_deployment:
+ continue
+
+ # check required properties for zip deployment
+ missing_properties = []
+ for p in required_properties:
+ if props.get(p) is None:
+ missing_properties.append(p)
+
+ if len(missing_properties) > 0:
+ message = "Properties {0} missing for zip file deployment at {1}"
+ matches.append(
+ RuleMatch(
+ path,
+ message.format(
+ missing_properties,
+ "/".join(
+ map(str, ["Resources", resource_name, "Properties"])
+ ),
+ ),
+ )
+ )
+
+ return matches
diff --git a/test/fixtures/templates/bad/resources/lambda/required_properties.yaml b/test/fixtures/templates/bad/resources/lambda/required_properties.yaml
new file mode 100644
index 0000000000..c087056343
--- /dev/null
+++ b/test/fixtures/templates/bad/resources/lambda/required_properties.yaml
@@ -0,0 +1,32 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Description: Missing properties.
+Resources:
+ Function1:
+ Type: AWS::Lambda::Function
+ Properties:
+# Handler: index.handler
+ Role: arn:aws:iam::123456789012:role/lambda-role
+ Code:
+ S3Bucket: my-bucket
+ S3Key: function.zip
+# Runtime: nodejs16.x
+ Function2:
+ Type: AWS::Lambda::Function
+ Properties:
+# Handler: index.handler
+ Role: arn:aws:iam::123456789012:role/lambda-role
+ Code:
+ S3Bucket: my-bucket
+ S3Key: function.zip
+ Runtime: python3.9
+ PackageType: Zip
+ Function3:
+ Type: AWS::Lambda::Function
+ Properties:
+ Handler: index.handler
+ Role: arn:aws:iam::123456789012:role/lambda-role
+ Code:
+ ZipFile: |
+ var aws = require('aws-sdk')
+ exports.handler = function(event, context) {}
+# Runtime: nodejs16.x
\ No newline at end of file
diff --git a/test/fixtures/templates/good/resources/lambda/required_properties.yaml b/test/fixtures/templates/good/resources/lambda/required_properties.yaml
new file mode 100644
index 0000000000..36823a575f
--- /dev/null
+++ b/test/fixtures/templates/good/resources/lambda/required_properties.yaml
@@ -0,0 +1,29 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Description: Required Properties.
+Resources:
+ Function1: # S3 key to deployment package
+ Type: AWS::Lambda::Function
+ Properties:
+ Handler: index.handler
+ Role: arn:aws:iam::123456789012:role/lambda-role
+ Code:
+ S3Bucket: my-bucket
+ S3Key: function.zip
+ Runtime: python3.9
+ Function2: # source inline (zipped by CloudFormation)
+ Type: AWS::Lambda::Function
+ Properties:
+ Handler: index.handler
+ Role: arn:aws:iam::123456789012:role/lambda-role
+ Code:
+ ZipFile: |
+ var aws = require('aws-sdk')
+ exports.handler = function(event, context) {}
+ Runtime: nodejs16.x
+ Function3: # image deployment
+ Type: AWS::Lambda::Function
+ Properties:
+ Role: arn:aws:iam::123456789012:role/lambda-role
+ Code:
+ ImageUri : 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
+ PackageType: Image
\ No newline at end of file
diff --git a/test/unit/rules/resources/lmbd/test_zip_package_required_properties.py b/test/unit/rules/resources/lmbd/test_zip_package_required_properties.py
new file mode 100644
index 0000000000..409b41beea
--- /dev/null
+++ b/test/unit/rules/resources/lmbd/test_zip_package_required_properties.py
@@ -0,0 +1,29 @@
+"""
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: MIT-0
+"""
+from test.unit.rules import BaseRuleTestCase
+
+from cfnlint.rules.resources.lmbd.ZipPackageRequiredProperties import (
+ ZipPackageRequiredProperties,
+)
+
+
+class TestZipPackageRequiredProperties(BaseRuleTestCase):
+ """Test required properties"""
+
+ def setUp(self):
+ super(TestZipPackageRequiredProperties, self).setUp()
+ self.collection.register(ZipPackageRequiredProperties())
+ self.success_templates = [
+ "test/fixtures/templates/good/resources/lambda/required_properties.yaml"
+ ]
+
+ def test_file_positive(self):
+ self.helper_file_positive()
+
+ def test_file_negative(self):
+ self.helper_file_negative(
+ "test/fixtures/templates/bad/resources/lambda/required_properties.yaml",
+ err_count=3,
+ )