Repository: bridgecrewio/checkov
Pull request: add Bicep graph
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Fixes #2632

- Changed `CKV_AZURE_23` to a graph check, didn't last long to be a Python check 😄 
- the current Bicep Graph supports YAML checks and connections, but not, if they are inside an interpolated string
- `context` was not added yet
- variable rendering for the graph will follow at some point, step by step 🐄 🐄 

Problem statement:
Bicep false CKV_AZURE_23
Hi,
I get CKV_AZURE_23 on this simple bicep file:

`
@description('SQL Logical server.')
param sqlLogicalServer object

@description('The SQL Logical Server password.')
@secure()
param password string

param tags object

var defaultAuditActionsAndGroups = [
  'SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP'
  'FAILED_DATABASE_AUTHENTICATION_GROUP'
  'BATCH_COMPLETED_GROUP'
]

resource sqlLogicalServerRes 'Microsoft.Sql/servers@2021-02-01-preview' = {
  name: sqlLogicalServer.name
  location: resourceGroup().location
  tags: tags
  identity: {
    type: sqlLogicalServer.systemManagedIdentity ? 'SystemAssigned' : 'None'
  }
  properties: {
    administratorLogin: sqlLogicalServer.userName
    administratorLoginPassword: password
    version: '12.0'
    minimalTlsVersion: sqlLogicalServer.minimalTlsVersion
    publicNetworkAccess: sqlLogicalServer.publicNetworkAccess
  }
}

// Audit settings need for enabling auditing to Log Analytics workspace
resource auditSettings 'Microsoft.Sql/servers/auditingSettings@2021-02-01-preview' = {
  name: 'default'
  parent: sqlLogicalServerRes
  properties: {
    state: 'Enabled'
    auditActionsAndGroups: defaultAuditActionsAndGroups
    storageEndpoint: ''
    storageAccountAccessKey: ''
    storageAccountSubscriptionId: '00000000-0000-0000-0000-000000000000'
    retentionDays: 0
    isAzureMonitorTargetEnabled: sqlLogicalServer.diagnosticLogsAndMetrics.auditLogs
    isDevopsAuditEnabled: sqlLogicalServer.diagnosticLogsAndMetrics.microsoftSupportOperationsAuditLogs
  }
}
`

My command is:
`checkov -f .\sql-logical-server.bicep --framework bicep`

Expected: no CKV_AZURE_23, since there is state: 'Enabled' in auditingSettings.properties
Version: 2.0.962

Hints from issue discussion:
hi @acorbos thanks for creating this issue and I'm happy that you gave the Bicep runner a try. It is still in an early stage and I'm improving it step by step.
@gruebel thanks for looking into it, I couldn't find the code. Is it somehow converting to ARM and then using the ARM checks?
I'm asking because I wanted to switch to ARM and I have the same problem with it.
See https://github.com/bridgecrewio/checkov/blob/master/checkov/arm/checks/resource/SQLServerAuditingEnabled.py
I think it should check on server level, not on database level if the message is "Ensure that 'Auditing' is set to 'Enabled' **for SQL servers**". By the way, that blank there... shouldn't it be a slash between MicrosoftSql and servers?
"Microsoft.Sql servers/databases/auditingSettings"

Why is it not working for me: This code expects a resource with type "auditingSettings" nested inside "Microsoft.Sql/servers". I have a resource with type "Microsoft.Sql/servers/auditingSettings" outside resource "Microsoft.Sql/servers". This is how the Azure portal exports templates. 

@acorbos you are right, but it is even a bit more complicated, but I'm working on a fix. It won't cover all possibilities, but it should be better than now 🙂

Commit message: add Bicep graph

Patch to review:
diff --git a/Pipfile b/Pipfile
index cba398da2a..9a8edeb9aa 100644
--- a/Pipfile
+++ b/Pipfile
@@ -67,7 +67,7 @@ aiomultiprocess = "*"
 jsonpath_ng = "*"
 jsonschema = "~=3.0"
 prettytable = ">=3.0.0"
-pycep-parser = "==0.3.2"
+pycep-parser = "==0.3.3"
 charset-normalizer = "*"
 
 [requires]
diff --git a/Pipfile.lock b/Pipfile.lock
index c8880eb6a2..71e77420b9 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "766dc3bce5ae9d802e087966a0c1a0aca5d8249f3f351b491949b492f8aa5ad7"
+            "sha256": "c756a63b73e8c3596ce9595169abe37c02e436e9ec8a92b327740634e3aa0809"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -160,19 +160,19 @@
         },
         "boto3": {
             "hashes": [
-                "sha256:76d5b90400c54b25278150768e946edf166acce2c1597c0ecfbebb1dbe9acf2c",
-                "sha256:7bb2e6506a6ad44d111dd20a5d510374b6958fe989b4ef887109c79d812f926f"
+                "sha256:44a30268702c78aa355a0044fa7fea5f1e7e7c125ad029196028787eea003c2e",
+                "sha256:c60d2401226c723db809df5c611a850c101b562e0e0790be7631a38b82813038"
             ],
             "index": "pypi",
-            "version": "==1.21.19"
+            "version": "==1.21.23"
         },
         "botocore": {
             "hashes": [
-                "sha256:5ed2be0e413961134f4c17eab16396d41a5b4b73a637588260c04d20806d52ea",
-                "sha256:d0d77bce152ca51f3c2cd0f9bf05cb3b623e719406ad58b4c20444e237fe82eb"
+                "sha256:1c8a77809dd59bf4084780ea1785c38d0ebcacd385d83fb7f6ad81648bbb22dc",
+                "sha256:536d5fcec2dfaf9671b9d361c2479ef435bfb905f73e24f6fbe73bc33ba66dfe"
             ],
             "markers": "python_version >= '3.6'",
-            "version": "==1.24.19"
+            "version": "==1.24.23"
         },
         "cached-property": {
             "hashes": [
@@ -272,7 +272,7 @@
                 "sha256:9653a2297357335d7325a1827e71ac1245d91c97d959346a7decabd4a52d5354",
                 "sha256:a6e924f3c46b657feb5b72679f7e930f8e5b224b766ab35c91ae4019b4e0615e"
             ],
-            "markers": "python_version >= '3.6' and python_version < '4.0'",
+            "markers": "python_version >= '3.6' and python_version < '4'",
             "version": "==0.5.3"
         },
         "cloudsplaining": {
@@ -469,11 +469,11 @@
         },
         "jmespath": {
             "hashes": [
-                "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
-                "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
+                "sha256:a490e280edd1f57d6de88636992d05b71e97d69a26a19f058ecf7d304474bf5e",
+                "sha256:e8dcd576ed616f14ec02eed0005c85973b5890083313860136657e24784e4c04"
             ],
             "index": "pypi",
-            "version": "==0.10.0"
+            "version": "==1.0.0"
         },
         "jsonpath-ng": {
             "hashes": [
@@ -516,49 +516,49 @@
         },
         "markupsafe": {
             "hashes": [
-                "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3",
-                "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8",
-                "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759",
-                "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed",
-                "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989",
-                "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3",
-                "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a",
-                "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c",
-                "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c",
-                "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8",
-                "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454",
-                "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad",
-                "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d",
-                "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635",
-                "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61",
-                "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea",
-                "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49",
-                "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce",
-                "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e",
-                "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f",
-                "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f",
-                "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f",
-                "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7",
-                "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a",
-                "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7",
-                "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076",
-                "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb",
-                "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7",
-                "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7",
-                "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c",
-                "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26",
-                "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c",
-                "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8",
-                "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448",
-                "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956",
-                "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05",
-                "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1",
-                "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357",
-                "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea",
-                "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730"
+                "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
+                "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
+                "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
+                "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
+                "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
+                "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
+                "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
+                "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
+                "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
+                "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
+                "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
+                "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
+                "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
+                "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
+                "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
+                "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
+                "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
+                "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
+                "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
+                "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
+                "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
+                "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
+                "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
+                "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
+                "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
+                "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
+                "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
+                "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
+                "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
+                "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
+                "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
+                "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
+                "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
+                "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
+                "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
+                "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
+                "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
+                "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
+                "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
+                "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
             ],
             "markers": "python_version >= '3.7'",
-            "version": "==2.1.0"
+            "version": "==2.1.1"
         },
         "multidict": {
             "hashes": [
@@ -718,11 +718,11 @@
         },
         "pycep-parser": {
             "hashes": [
-                "sha256:88f6926ee907d5f233284bb25d591fdac0ca7292b3487e7f951b1ec489521635",
-                "sha256:b81da2a13e95fea3b331455476f99b2004f58f554c2bf65c3d368f2b223be57f"
+                "sha256:b45bfef3410ddf77122ef7b618b5ab5d724f07696e48b2fc2703a649bec8277e",
+                "sha256:f5523ed72891228b68fe8f8f5564b6a3113e76297d5373729b23ca0f1a58b6db"
             ],
             "index": "pypi",
-            "version": "==0.3.2"
+            "version": "==0.3.3"
         },
         "pycparser": {
             "hashes": [
@@ -815,83 +815,83 @@
         },
         "regex": {
             "hashes": [
-                "sha256:0008650041531d0eadecc96a73d37c2dc4821cf51b0766e374cb4f1ddc4e1c14",
-                "sha256:03299b0bcaa7824eb7c0ebd7ef1e3663302d1b533653bfe9dc7e595d453e2ae9",
-                "sha256:06b1df01cf2aef3a9790858af524ae2588762c8a90e784ba00d003f045306204",
-                "sha256:09b4b6ccc61d4119342b26246ddd5a04accdeebe36bdfe865ad87a0784efd77f",
-                "sha256:0be0c34a39e5d04a62fd5342f0886d0e57592a4f4993b3f9d257c1f688b19737",
-                "sha256:0d96eec8550fd2fd26f8e675f6d8b61b159482ad8ffa26991b894ed5ee19038b",
-                "sha256:0eb0e2845e81bdea92b8281a3969632686502565abf4a0b9e4ab1471c863d8f3",
-                "sha256:13bbf0c9453c6d16e5867bda7f6c0c7cff1decf96c5498318bb87f8136d2abd4",
-                "sha256:17e51ad1e6131c496b58d317bc9abec71f44eb1957d32629d06013a21bc99cac",
-                "sha256:1977bb64264815d3ef016625adc9df90e6d0e27e76260280c63eca993e3f455f",
-                "sha256:1e30762ddddb22f7f14c4f59c34d3addabc789216d813b0f3e2788d7bcf0cf29",
-                "sha256:1e73652057473ad3e6934944af090852a02590c349357b79182c1b681da2c772",
-                "sha256:20e6a27959f162f979165e496add0d7d56d7038237092d1aba20b46de79158f1",
-                "sha256:286ff9ec2709d56ae7517040be0d6c502642517ce9937ab6d89b1e7d0904f863",
-                "sha256:297c42ede2c81f0cb6f34ea60b5cf6dc965d97fa6936c11fc3286019231f0d66",
-                "sha256:320c2f4106962ecea0f33d8d31b985d3c185757c49c1fb735501515f963715ed",
-                "sha256:35ed2f3c918a00b109157428abfc4e8d1ffabc37c8f9abc5939ebd1e95dabc47",
-                "sha256:3d146e5591cb67c5e836229a04723a30af795ef9b70a0bbd913572e14b7b940f",
-                "sha256:42bb37e2b2d25d958c25903f6125a41aaaa1ed49ca62c103331f24b8a459142f",
-                "sha256:42d6007722d46bd2c95cce700181570b56edc0dcbadbfe7855ec26c3f2d7e008",
-                "sha256:43eba5c46208deedec833663201752e865feddc840433285fbadee07b84b464d",
-                "sha256:452519bc4c973e961b1620c815ea6dd8944a12d68e71002be5a7aff0a8361571",
-                "sha256:4b9c16a807b17b17c4fa3a1d8c242467237be67ba92ad24ff51425329e7ae3d0",
-                "sha256:5510932596a0f33399b7fff1bd61c59c977f2b8ee987b36539ba97eb3513584a",
-                "sha256:55820bc631684172b9b56a991d217ec7c2e580d956591dc2144985113980f5a3",
-                "sha256:57484d39447f94967e83e56db1b1108c68918c44ab519b8ecfc34b790ca52bf7",
-                "sha256:58ba41e462653eaf68fc4a84ec4d350b26a98d030be1ab24aba1adcc78ffe447",
-                "sha256:5bc5f921be39ccb65fdda741e04b2555917a4bced24b4df14eddc7569be3b493",
-                "sha256:5dcc4168536c8f68654f014a3db49b6b4a26b226f735708be2054314ed4964f4",
-                "sha256:5f92a7cdc6a0ae2abd184e8dfd6ef2279989d24c85d2c85d0423206284103ede",
-                "sha256:67250b36edfa714ba62dc62d3f238e86db1065fccb538278804790f578253640",
-                "sha256:6df070a986fc064d865c381aecf0aaff914178fdf6874da2f2387e82d93cc5bd",
-                "sha256:729aa8ca624c42f309397c5fc9e21db90bf7e2fdd872461aabdbada33de9063c",
-                "sha256:72bc3a5effa5974be6d965ed8301ac1e869bc18425c8a8fac179fbe7876e3aee",
-                "sha256:74d86e8924835f863c34e646392ef39039405f6ce52956d8af16497af4064a30",
-                "sha256:79e5af1ff258bc0fe0bdd6f69bc4ae33935a898e3cbefbbccf22e88a27fa053b",
-                "sha256:7b103dffb9f6a47ed7ffdf352b78cfe058b1777617371226c1894e1be443afec",
-                "sha256:83f03f0bd88c12e63ca2d024adeee75234d69808b341e88343b0232329e1f1a1",
-                "sha256:86d7a68fa53688e1f612c3246044157117403c7ce19ebab7d02daf45bd63913e",
-                "sha256:878c626cbca3b649e14e972c14539a01191d79e58934e3f3ef4a9e17f90277f8",
-                "sha256:878f5d649ba1db9f52cc4ef491f7dba2d061cdc48dd444c54260eebc0b1729b9",
-                "sha256:87bc01226cd288f0bd9a4f9f07bf6827134dc97a96c22e2d28628e824c8de231",
-                "sha256:8babb2b5751105dc0aef2a2e539f4ba391e738c62038d8cb331c710f6b0f3da7",
-                "sha256:91e0f7e7be77250b808a5f46d90bf0032527d3c032b2131b63dee54753a4d729",
-                "sha256:9557545c10d52c845f270b665b52a6a972884725aa5cf12777374e18f2ea8960",
-                "sha256:9ccb0a4ab926016867260c24c192d9df9586e834f5db83dfa2c8fffb3a6e5056",
-                "sha256:9d828c5987d543d052b53c579a01a52d96b86f937b1777bbfe11ef2728929357",
-                "sha256:9efa41d1527b366c88f265a227b20bcec65bda879962e3fc8a2aee11e81266d7",
-                "sha256:aaf5317c961d93c1a200b9370fb1c6b6836cc7144fef3e5a951326912bf1f5a3",
-                "sha256:ab69b4fe09e296261377d209068d52402fb85ef89dc78a9ac4a29a895f4e24a7",
-                "sha256:ad397bc7d51d69cb07ef89e44243f971a04ce1dca9bf24c992c362406c0c6573",
-                "sha256:ae17fc8103f3b63345709d3e9654a274eee1c6072592aec32b026efd401931d0",
-                "sha256:af4d8cc28e4c7a2f6a9fed544228c567340f8258b6d7ea815b62a72817bbd178",
-                "sha256:b22ff939a8856a44f4822da38ef4868bd3a9ade22bb6d9062b36957c850e404f",
-                "sha256:b549d851f91a4efb3e65498bd4249b1447ab6035a9972f7fc215eb1f59328834",
-                "sha256:be319f4eb400ee567b722e9ea63d5b2bb31464e3cf1b016502e3ee2de4f86f5c",
-                "sha256:c0446b2871335d5a5e9fcf1462f954586b09a845832263db95059dcd01442015",
-                "sha256:c68d2c04f7701a418ec2e5631b7f3552efc32f6bcc1739369c6eeb1af55f62e0",
-                "sha256:c87ac58b9baaf50b6c1b81a18d20eda7e2883aa9a4fb4f1ca70f2e443bfcdc57",
-                "sha256:caa2734ada16a44ae57b229d45091f06e30a9a52ace76d7574546ab23008c635",
-                "sha256:cb34c2d66355fb70ae47b5595aafd7218e59bb9c00ad8cc3abd1406ca5874f07",
-                "sha256:cb3652bbe6720786b9137862205986f3ae54a09dec8499a995ed58292bdf77c2",
-                "sha256:cf668f26604e9f7aee9f8eaae4ca07a948168af90b96be97a4b7fa902a6d2ac1",
-                "sha256:d326ff80ed531bf2507cba93011c30fff2dd51454c85f55df0f59f2030b1687b",
-                "sha256:d6c2441538e4fadd4291c8420853431a229fcbefc1bf521810fbc2629d8ae8c2",
-                "sha256:d6ecfd1970b3380a569d7b3ecc5dd70dba295897418ed9e31ec3c16a5ab099a5",
-                "sha256:e5602a9b5074dcacc113bba4d2f011d2748f50e3201c8139ac5b68cf2a76bd8b",
-                "sha256:ef806f684f17dbd6263d72a54ad4073af42b42effa3eb42b877e750c24c76f86",
-                "sha256:f3356afbb301ec34a500b8ba8b47cba0b44ed4641c306e1dd981a08b416170b5",
-                "sha256:f6f7ee2289176cb1d2c59a24f50900f8b9580259fa9f1a739432242e7d254f93",
-                "sha256:f7e8f1ee28e0a05831c92dc1c0c1c94af5289963b7cf09eca5b5e3ce4f8c91b0",
-                "sha256:f8169ec628880bdbca67082a9196e2106060a4a5cbd486ac51881a4df805a36f",
-                "sha256:fbc88d3ba402b5d041d204ec2449c4078898f89c4a6e6f0ed1c1a510ef1e221d",
-                "sha256:fbd3fe37353c62fd0eb19fb76f78aa693716262bcd5f9c14bb9e5aca4b3f0dc4"
+                "sha256:0066a6631c92774391f2ea0f90268f0d82fffe39cb946f0f9c6b382a1c61a5e5",
+                "sha256:0100f0ded953b6b17f18207907159ba9be3159649ad2d9b15535a74de70359d3",
+                "sha256:01c913cf573d1da0b34c9001a94977273b5ee2fe4cb222a5d5b320f3a9d1a835",
+                "sha256:0214ff6dff1b5a4b4740cfe6e47f2c4c92ba2938fca7abbea1359036305c132f",
+                "sha256:029e9e7e0d4d7c3446aa92474cbb07dafb0b2ef1d5ca8365f059998c010600e6",
+                "sha256:0317eb6331146c524751354ebef76a7a531853d7207a4d760dfb5f553137a2a4",
+                "sha256:04b5ee2b6d29b4a99d38a6469aa1db65bb79d283186e8460542c517da195a8f6",
+                "sha256:04c09b9651fa814eeeb38e029dc1ae83149203e4eeb94e52bb868fadf64852bc",
+                "sha256:058054c7a54428d5c3e3739ac1e363dc9347d15e64833817797dc4f01fb94bb8",
+                "sha256:060f9066d2177905203516c62c8ea0066c16c7342971d54204d4e51b13dfbe2e",
+                "sha256:0a7b75cc7bb4cc0334380053e4671c560e31272c9d2d5a6c4b8e9ae2c9bd0f82",
+                "sha256:0e2630ae470d6a9f8e4967388c1eda4762706f5750ecf387785e0df63a4cc5af",
+                "sha256:174d964bc683b1e8b0970e1325f75e6242786a92a22cedb2a6ec3e4ae25358bd",
+                "sha256:25ecb1dffc5e409ca42f01a2b2437f93024ff1612c1e7983bad9ee191a5e8828",
+                "sha256:286908cbe86b1a0240a867aecfe26a439b16a1f585d2de133540549831f8e774",
+                "sha256:303b15a3d32bf5fe5a73288c316bac5807587f193ceee4eb6d96ee38663789fa",
+                "sha256:34bb30c095342797608727baf5c8aa122406aa5edfa12107b8e08eb432d4c5d7",
+                "sha256:3e265b388cc80c7c9c01bb4f26c9e536c40b2c05b7231fbb347381a2e1c8bf43",
+                "sha256:3e4d710ff6539026e49f15a3797c6b1053573c2b65210373ef0eec24480b900b",
+                "sha256:42eb13b93765c6698a5ab3bcd318d8c39bb42e5fa8a7fcf7d8d98923f3babdb1",
+                "sha256:48081b6bff550fe10bcc20c01cf6c83dbca2ccf74eeacbfac240264775fd7ecf",
+                "sha256:491fc754428514750ab21c2d294486223ce7385446f2c2f5df87ddbed32979ae",
+                "sha256:4d1445824944e642ffa54c4f512da17a953699c563a356d8b8cbdad26d3b7598",
+                "sha256:530a3a16e57bd3ea0dff5ec2695c09632c9d6c549f5869d6cf639f5f7153fb9c",
+                "sha256:591d4fba554f24bfa0421ba040cd199210a24301f923ed4b628e1e15a1001ff4",
+                "sha256:5a86cac984da35377ca9ac5e2e0589bd11b3aebb61801204bd99c41fac516f0d",
+                "sha256:5b1ceede92400b3acfebc1425937454aaf2c62cd5261a3fabd560c61e74f6da3",
+                "sha256:5b2e24f3ae03af3d8e8e6d824c891fea0ca9035c5d06ac194a2700373861a15c",
+                "sha256:6504c22c173bb74075d7479852356bb7ca80e28c8e548d4d630a104f231e04fb",
+                "sha256:673f5a393d603c34477dbad70db30025ccd23996a2d0916e942aac91cc42b31a",
+                "sha256:6ca6dcd17f537e9f3793cdde20ac6076af51b2bd8ad5fe69fa54373b17b48d3c",
+                "sha256:6e1d8ed9e61f37881c8db383a124829a6e8114a69bd3377a25aecaeb9b3538f8",
+                "sha256:75a5e6ce18982f0713c4bac0704bf3f65eed9b277edd3fb9d2b0ff1815943327",
+                "sha256:76435a92e444e5b8f346aed76801db1c1e5176c4c7e17daba074fbb46cb8d783",
+                "sha256:764e66a0e382829f6ad3bbce0987153080a511c19eb3d2f8ead3f766d14433ac",
+                "sha256:78ce90c50d0ec970bd0002462430e00d1ecfd1255218d52d08b3a143fe4bde18",
+                "sha256:794a6bc66c43db8ed06698fc32aaeaac5c4812d9f825e9589e56f311da7becd9",
+                "sha256:797437e6024dc1589163675ae82f303103063a0a580c6fd8d0b9a0a6708da29e",
+                "sha256:7b7494df3fdcc95a1f76cf134d00b54962dd83189520fd35b8fcd474c0aa616d",
+                "sha256:7d1a6e403ac8f1d91d8f51c441c3f99367488ed822bda2b40836690d5d0059f5",
+                "sha256:7f63877c87552992894ea1444378b9c3a1d80819880ae226bb30b04789c0828c",
+                "sha256:8923e1c5231549fee78ff9b2914fad25f2e3517572bb34bfaa3aea682a758683",
+                "sha256:8afcd1c2297bc989dceaa0379ba15a6df16da69493635e53431d2d0c30356086",
+                "sha256:8b1cc70e31aacc152a12b39245974c8fccf313187eead559ee5966d50e1b5817",
+                "sha256:8d1f3ea0d1924feb4cf6afb2699259f658a08ac6f8f3a4a806661c2dfcd66db1",
+                "sha256:940570c1a305bac10e8b2bc934b85a7709c649317dd16520471e85660275083a",
+                "sha256:947a8525c0a95ba8dc873191f9017d1b1e3024d4dc757f694e0af3026e34044a",
+                "sha256:9beb03ff6fe509d6455971c2489dceb31687b38781206bcec8e68bdfcf5f1db2",
+                "sha256:9c144405220c5ad3f5deab4c77f3e80d52e83804a6b48b6bed3d81a9a0238e4c",
+                "sha256:a98ae493e4e80b3ded6503ff087a8492db058e9c68de371ac3df78e88360b374",
+                "sha256:aa2ce79f3889720b46e0aaba338148a1069aea55fda2c29e0626b4db20d9fcb7",
+                "sha256:aa5eedfc2461c16a092a2fabc5895f159915f25731740c9152a1b00f4bcf629a",
+                "sha256:ab5d89cfaf71807da93c131bb7a19c3e19eaefd613d14f3bce4e97de830b15df",
+                "sha256:b4829db3737480a9d5bfb1c0320c4ee13736f555f53a056aacc874f140e98f64",
+                "sha256:b52771f05cff7517f7067fef19ffe545b1f05959e440d42247a17cd9bddae11b",
+                "sha256:b8248f19a878c72d8c0a785a2cd45d69432e443c9f10ab924c29adda77b324ae",
+                "sha256:b9809404528a999cf02a400ee5677c81959bc5cb938fdc696b62eb40214e3632",
+                "sha256:c155a1a80c5e7a8fa1d9bb1bf3c8a953532b53ab1196092749bafb9d3a7cbb60",
+                "sha256:c33ce0c665dd325200209340a88438ba7a470bd5f09f7424e520e1a3ff835b52",
+                "sha256:c5adc854764732dbd95a713f2e6c3e914e17f2ccdc331b9ecb777484c31f73b6",
+                "sha256:cb374a2a4dba7c4be0b19dc7b1adc50e6c2c26c3369ac629f50f3c198f3743a4",
+                "sha256:cd00859291658fe1fda48a99559fb34da891c50385b0bfb35b808f98956ef1e7",
+                "sha256:ce3057777a14a9a1399b81eca6a6bfc9612047811234398b84c54aeff6d536ea",
+                "sha256:d0a5a1fdc9f148a8827d55b05425801acebeeefc9e86065c7ac8b8cc740a91ff",
+                "sha256:dad3991f0678facca1a0831ec1ddece2eb4d1dd0f5150acb9440f73a3b863907",
+                "sha256:dc7b7c16a519d924c50876fb152af661a20749dcbf653c8759e715c1a7a95b18",
+                "sha256:dcbb7665a9db9f8d7642171152c45da60e16c4f706191d66a1dc47ec9f820aed",
+                "sha256:df037c01d68d1958dad3463e2881d3638a0d6693483f58ad41001aa53a83fcea",
+                "sha256:f08a7e4d62ea2a45557f561eea87c907222575ca2134180b6974f8ac81e24f06",
+                "sha256:f16cf7e4e1bf88fecf7f41da4061f181a6170e179d956420f84e700fb8a3fd6b",
+                "sha256:f2c53f3af011393ab5ed9ab640fa0876757498aac188f782a0c620e33faa2a3d",
+                "sha256:f320c070dea3f20c11213e56dbbd7294c05743417cde01392148964b7bc2d31a",
+                "sha256:f553a1190ae6cd26e553a79f6b6cfba7b8f304da2071052fa33469da075ea625",
+                "sha256:fc8c7958d14e8270171b3d72792b609c057ec0fa17d507729835b5cff6b7f69a"
             ],
             "markers": "python_version >= '3.6'",
-            "version": "==2022.3.2"
+            "version": "==2022.3.15"
         },
         "requests": {
             "hashes": [
@@ -924,6 +924,14 @@
             "index": "pypi",
             "version": "==2.9.0"
         },
+        "setuptools": {
+            "hashes": [
+                "sha256:6599055eeb23bfef457d5605d33a4d68804266e6cb430b0fb12417c5efeae36c",
+                "sha256:782ef48d58982ddb49920c11a0c5c9c0b02e7d7d1c2ad0aa44e1a1e133051c96"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==60.10.0"
+        },
         "six": {
             "hashes": [
                 "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
@@ -981,10 +989,10 @@
         },
         "types-setuptools": {
             "hashes": [
-                "sha256:9a13513679c640f6616e2d9ab50d431c99ca8ae9848a97243f887c80fd5cf294",
-                "sha256:ddc98da82c12e1208012d65276641a132d3aadc78ecfff68fd3e17d85933a3c1"
+                "sha256:262f7406e0c7d705ad6bb4526b5b761fa500bf99eab74de85ac3592187d62935",
+                "sha256:fbb0647569d6fb2f6bc472402265250c0ffa53e60180d2cbfee9e84f085921f0"
             ],
-            "version": "==57.4.10"
+            "version": "==57.4.11"
         },
         "types-toml": {
             "hashes": [
@@ -1011,11 +1019,11 @@
         },
         "urllib3": {
             "hashes": [
-                "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
-                "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
+                "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
+                "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
             ],
-            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'",
-            "version": "==1.26.8"
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+            "version": "==1.26.9"
         },
         "wcwidth": {
             "hashes": [
@@ -1270,9 +1278,7 @@
             "version": "==2.0.12"
         },
         "coverage": {
-            "extras": [
-                "toml"
-            ],
+            "extras": [],
             "hashes": [
                 "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c",
                 "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6",
@@ -1459,11 +1465,11 @@
         },
         "identify": {
             "hashes": [
-                "sha256:2986942d3974c8f2e5019a190523b0b0e2a07cb8e89bf236727fb4b26f27f8fd",
-                "sha256:fd906823ed1db23c7a48f9b176a1d71cb8abede1e21ebe614bac7bdd688d9213"
+                "sha256:3f3244a559290e7d3deb9e9adc7b33594c1bc85a9dd82e0f1be519bf12a1ec17",
+                "sha256:5f06b14366bd1facb88b00540a1de05b69b310cbc2654db3c7e07fa3a4339323"
             ],
             "markers": "python_version >= '3.7'",
-            "version": "==2.4.11"
+            "version": "==2.4.12"
         },
         "idna": {
             "hashes": [
@@ -1684,11 +1690,11 @@
         },
         "pytest": {
             "hashes": [
-                "sha256:b555252a95bbb2a37a97b5ac2eb050c436f7989993565f5e0c9128fcaacadd0e",
-                "sha256:f1089d218cfcc63a212c42896f1b7fbf096874d045e1988186861a1a87d27b47"
+                "sha256:841132caef6b1ad17a9afde46dc4f6cfa59a05f9555aae5151f73bdf2820ca63",
+                "sha256:92f723789a8fdd7180b6b06483874feca4c48a5c76968e03bb3e7f806a1869ea"
             ],
             "index": "pypi",
-            "version": "==7.1.0"
+            "version": "==7.1.1"
         },
         "pytest-asyncio": {
             "hashes": [
@@ -1779,11 +1785,11 @@
         },
         "responses": {
             "hashes": [
-                "sha256:3fc29c3117e14136b833a0a6d4e7f1217c6301bf08b6086db468e12f1e3290e2",
-                "sha256:53354b5de163aa2074312c71d8ebccb8bd1ab336cff7053abb75e84dc5637abe"
+                "sha256:18831bc2d72443b67664d98038374a6fa1f27eaaff4dd9a7d7613723416fea3c",
+                "sha256:644905bc4fb8a18fa37e3882b2ac05e610fe8c2f967d327eed669e314d94a541"
             ],
             "index": "pypi",
-            "version": "==0.19.0"
+            "version": "==0.20.0"
         },
         "six": {
             "hashes": [
@@ -1827,19 +1833,19 @@
         },
         "types-pyyaml": {
             "hashes": [
-                "sha256:6252f62d785e730e454dfa0c9f0fb99d8dae254c5c3c686903cf878ea27c04b7",
-                "sha256:693b01c713464a6851f36ff41077f8adbc6e355eda929addfb4a97208aea9b4b"
+                "sha256:2fd21310870addfd51db621ad9f3b373f33ee3cbb81681d70ef578760bd22d35",
+                "sha256:464e050914f3d1d83a8c038e1cf46da5cb96b7cd02eaa096bcaa03675edd8a2e"
             ],
             "index": "pypi",
-            "version": "==6.0.4"
+            "version": "==6.0.5"
         },
         "types-requests": {
             "hashes": [
-                "sha256:120c949953b618e334bbe78de38e65aa261e1f48df021a05f0be833a848e4ba7",
-                "sha256:fd1382fa2e28eac848faedb0332840204f06f0cb517008e3c7b8282ca53e56d2"
+                "sha256:04579ee164f7c2659be46950e3c2f8d51a081ad252ef1b01d4b12faba5c3810b",
+                "sha256:c01838abfe3e8a83ba68346cd373afff97594c19c15c922ddee4a0e80ba7e329"
             ],
             "index": "pypi",
-            "version": "==2.27.12"
+            "version": "==2.27.14"
         },
         "types-urllib3": {
             "hashes": [
@@ -1850,11 +1856,11 @@
         },
         "urllib3": {
             "hashes": [
-                "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed",
-                "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"
+                "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
+                "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
             ],
-            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'",
-            "version": "==1.26.8"
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+            "version": "==1.26.9"
         },
         "urllib3-mock": {
             "hashes": [
@@ -1866,11 +1872,11 @@
         },
         "virtualenv": {
             "hashes": [
-                "sha256:dd448d1ded9f14d1a4bfa6bfc0c5b96ae3be3f2d6c6c159b23ddcfd701baa021",
-                "sha256:e9dd1a1359d70137559034c0f5433b34caf504af2dc756367be86a5a32967134"
+                "sha256:c3e01300fb8495bc00ed70741f5271fc95fed067eb7106297be73d30879af60c",
+                "sha256:ce8901d3bbf3b90393498187f2d56797a8a452fb2d0d7efc6fd837554d6f679c"
             ],
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
-            "version": "==20.13.3"
+            "version": "==20.13.4"
         },
         "yarl": {
             "hashes": [
diff --git a/checkov/bicep/checks/graph_checks/SQLServerAuditingEnabled.yaml b/checkov/bicep/checks/graph_checks/SQLServerAuditingEnabled.yaml
new file mode 100644
index 0000000000..22b181575d
--- /dev/null
+++ b/checkov/bicep/checks/graph_checks/SQLServerAuditingEnabled.yaml
@@ -0,0 +1,39 @@
+metadata:
+  id: "CKV_AZURE_23"
+  name: "Ensure that 'Auditing' is set to 'On' for SQL servers"
+  category: "LOGGING"
+definition:
+  and:
+    - cond_type: filter
+      attribute: resource_type
+      operator: within
+      value:
+        - Microsoft.Sql/servers
+        - Microsoft.Sql/servers/databases
+    - or:
+        - and:
+            - cond_type: connection
+              resource_types:
+                - Microsoft.Sql/servers
+              connected_resource_types:
+                - Microsoft.Sql/servers/auditingSettings
+              operator: exists
+            - cond_type: attribute
+              resource_types:
+                - Microsoft.Sql/servers/auditingSettings
+              attribute: properties.state
+              operator: equals
+              value: Enabled
+        - and:
+            - cond_type: connection
+              resource_types:
+                - Microsoft.Sql/servers/databases
+              connected_resource_types:
+                - Microsoft.Sql/servers/databases/auditingSettings
+              operator: exists
+            - cond_type: attribute
+              resource_types:
+                - Microsoft.Sql/servers/databases/auditingSettings
+              attribute: properties.state
+              operator: equals
+              value: Enabled
diff --git a/checkov/bicep/checks/resource/azure/SQLServerAuditingEnabled.py b/checkov/bicep/checks/resource/azure/SQLServerAuditingEnabled.py
deleted file mode 100644
index e1cc516f35..0000000000
--- a/checkov/bicep/checks/resource/azure/SQLServerAuditingEnabled.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from typing import Any
-
-from checkov.bicep.checks.resource.base_resource_value_check import BaseResourceValueCheck
-from checkov.common.models.enums import CheckCategories
-
-
-class SQLServerAuditingEnabled(BaseResourceValueCheck):
-    # this should be a graph check, due to the possible connection between
-    # Microsoft.Sql/servers -> Microsoft.Sql/servers/auditingSettings
-    # Microsoft.Sql/servers -> Microsoft.Sql/servers/databases/auditingSettings
-
-    def __init__(self) -> None:
-        name = "Ensure that 'Auditing' is set to 'Enabled' for SQL servers"
-        id = "CKV_AZURE_23"
-        supported_resources = (
-            "Microsoft.Sql/servers/auditingSettings",
-            "Microsoft.Sql/servers/databases/auditingSettings",
-        )
-        categories = (CheckCategories.LOGGING,)
-        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
-
-    def get_inspected_key(self) -> str:
-        return "properties/state"
-
-    def get_expected_value(self) -> Any:
-        return "Enabled"
-
-
-check = SQLServerAuditingEnabled()
diff --git a/checkov/bicep/checks/resource/base_registry.py b/checkov/bicep/checks/resource/base_registry.py
index 600d2e0cc6..eddf181450 100644
--- a/checkov/bicep/checks/resource/base_registry.py
+++ b/checkov/bicep/checks/resource/base_registry.py
@@ -6,6 +6,8 @@
 from checkov.common.checks.base_check_registry import BaseCheckRegistry
 from checkov.runner_filter import RunnerFilter
 
+GRAPH_CHECK_IDS = ("CKV_AZURE_23",)
+
 
 class Registry(BaseCheckRegistry):
     def __init__(self) -> None:
@@ -18,6 +20,10 @@ def register(self, check: BaseCheck) -> None:
         if self._BaseCheckRegistry__loading_external_checks:
             RunnerFilter.notify_external_check(check.id)
 
+        # don't add an ARM check, if a Bicep graph check exists for it
+        if check.id in GRAPH_CHECK_IDS:
+            return
+
         # remove the ARM check, if a Bicep check with the same check ID exists
         if check.id in self.check_id_to_enitity_map.keys():
             if check.__module__.split(".")[1] != "bicep":
diff --git a/checkov/bicep/graph_builder/__init__.py b/checkov/bicep/graph_builder/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/checkov/bicep/graph_builder/graph_components/__init__.py b/checkov/bicep/graph_builder/graph_components/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/checkov/bicep/graph_builder/graph_components/block_types.py b/checkov/bicep/graph_builder/graph_components/block_types.py
new file mode 100644
index 0000000000..fd66ce9d57
--- /dev/null
+++ b/checkov/bicep/graph_builder/graph_components/block_types.py
@@ -0,0 +1,14 @@
+from dataclasses import dataclass
+
+from typing_extensions import Literal
+
+from checkov.common.graph.graph_builder.graph_components.block_types import BlockType as CommonBlockType
+
+
+@dataclass
+class BlockType(CommonBlockType):
+    TARGET_SCOPE: Literal["targetScope"] = "targetScope"
+    PARAM: Literal["param"] = "param"
+    VAR: Literal["var"] = "var"
+    MODULE: Literal["module"] = "module"
+    OUTPUT: Literal["output"] = "output"
diff --git a/checkov/bicep/graph_builder/graph_components/blocks.py b/checkov/bicep/graph_builder/graph_components/blocks.py
new file mode 100644
index 0000000000..18ad0eae42
--- /dev/null
+++ b/checkov/bicep/graph_builder/graph_components/blocks.py
@@ -0,0 +1,20 @@
+from __future__ import annotations
+
+from typing import Any
+
+from typing_extensions import Literal
+
+from checkov.common.graph.graph_builder.graph_components.blocks import Block
+
+
+class BicepBlock(Block):
+    def __init__(
+        self,
+        name: str,
+        config: dict[str, Any],
+        path: str,
+        block_type: str,
+        attributes: dict[str, Any],
+        id: str = "",
+    ) -> None:
+        super().__init__(name, config, path, block_type, attributes, id, "Bicep")
diff --git a/checkov/bicep/graph_builder/local_graph.py b/checkov/bicep/graph_builder/local_graph.py
new file mode 100644
index 0000000000..833d7f94fd
--- /dev/null
+++ b/checkov/bicep/graph_builder/local_graph.py
@@ -0,0 +1,234 @@
+from __future__ import annotations
+
+import logging
+from copy import deepcopy
+from enum import Enum
+from pathlib import Path
+from typing import Any, TYPE_CHECKING
+
+from pycep.transformer import BicepElement
+from pycep.typing import (
+    BicepJson,
+    ResourceAttributes,
+    GlobalsAttributes,
+    ParameterAttributes,
+    VariableAttributes,
+    OutputAttributes,
+)
+from typing_extensions import Literal
+
+from checkov.bicep.graph_builder.graph_components.block_types import BlockType
+from checkov.bicep.graph_builder.graph_components.blocks import BicepBlock
+from checkov.common.graph.graph_builder.graph_components.edge import Edge
+from checkov.common.graph.graph_builder.local_graph import LocalGraph
+
+if TYPE_CHECKING:
+    from checkov.common.graph.graph_builder.graph_components.blocks import Block
+
+BICEP_ELEMENT_TO_BLOCK_TYPE_MAP: dict[str, Literal["param", "var", "resource", "module", "output"]] = {
+    "modules": BlockType.MODULE,
+}
+
+
+class BicepElements(str, Enum):
+    GLOBALS = "globals"
+    PARAMETERS = "parameters"
+    VARIABLES = "variables"
+    RESOURCES = "resources"
+    OUTPUTS = "outputs"
+
+
+class BicepLocalGraph(LocalGraph):
+    def __init__(self, definitions: dict[Path, BicepJson]) -> None:
+        super().__init__()
+        self.definitions = definitions
+        self.vertices_by_name: dict[str, int] = {}
+
+    def build_graph(self, render_variables: bool) -> None:
+        self._create_vertices()
+        logging.info(f"[BicepLocalGraph] created {len(self.vertices)} vertices")
+        self._create_edges()
+        logging.info(f"[BicepLocalGraph] created {len(self.edges)} edges")
+
+    def _create_vertices(self) -> None:
+        for file_path, bicep_conf in self.definitions.items():
+            self._create_global_vertices(file_path=file_path, globals_attrs=bicep_conf.get(BicepElements.GLOBALS))
+            self._create_param_vertices(file_path=file_path, parameters=bicep_conf.get(BicepElements.PARAMETERS))
+            self._create_var_vertices(file_path=file_path, variables=bicep_conf.get(BicepElements.VARIABLES))
+            self._create_resource_vertices(file_path=file_path, resources=bicep_conf.get(BicepElements.RESOURCES))
+            self._create_output_vertices(file_path=file_path, outputs=bicep_conf.get(BicepElements.OUTPUTS))
+
+            for element_type, elements in bicep_conf.items():
+                if element_type in BicepElements.__members__.values():
+                    # already processed above
+                    continue
+
+                for name, conf in elements.items():
+                    config = deepcopy(conf)
+                    block_type = BICEP_ELEMENT_TO_BLOCK_TYPE_MAP[element_type]
+                    attributes = deepcopy(config)
+
+                    self.vertices.append(
+                        BicepBlock(
+                            name=name,
+                            config=config,
+                            path=str(file_path),
+                            block_type=block_type,
+                            attributes=attributes,
+                            id=f"{block_type}.{name}",
+                        )
+                    )
+
+        for i, vertex in enumerate(self.vertices):
+            self.vertices_by_block_type[vertex.block_type].append(i)
+            self.vertices_block_name_map[vertex.block_type][vertex.name].append(i)
+            self.vertices_by_name[vertex.name] = i
+
+    def _create_global_vertices(self, file_path: Path, globals_attrs: GlobalsAttributes | None) -> None:
+        if not globals_attrs:
+            return
+
+        # there can only be one target scope per file
+        config = deepcopy(globals_attrs["scope"])
+        attributes = deepcopy(config)
+
+        self.vertices.append(
+            BicepBlock(
+                name=BlockType.TARGET_SCOPE,
+                config=config,  # type:ignore[arg-type]
+                path=str(file_path),
+                block_type=BlockType.TARGET_SCOPE,
+                attributes=attributes,  # type:ignore[arg-type]
+                id=BlockType.TARGET_SCOPE,
+            )
+        )
+
+    def _create_param_vertices(self, file_path: Path, parameters: dict[str, ParameterAttributes] | None) -> None:
+        if not parameters:
+            return
+
+        for name, conf in parameters.items():
+            config = deepcopy(conf)
+            attributes = deepcopy(conf)
+
+            self.vertices.append(
+                BicepBlock(
+                    name=name,
+                    config=config,  # type:ignore[arg-type]
+                    path=str(file_path),
+                    block_type=BlockType.PARAM,
+                    attributes=attributes,  # type:ignore[arg-type]
+                    id=f"{BlockType.PARAM}.{name}",
+                )
+            )
+
+    def _create_var_vertices(self, file_path: Path, variables: dict[str, VariableAttributes] | None) -> None:
+        if not variables:
+            return
+
+        for name, conf in variables.items():
+            config = deepcopy(conf)
+            attributes = deepcopy(conf)
+
+            self.vertices.append(
+                BicepBlock(
+                    name=name,
+                    config=config,  # type:ignore[arg-type]
+                    path=str(file_path),
+                    block_type=BlockType.VAR,
+                    attributes=attributes,  # type:ignore[arg-type]
+                    id=f"{BlockType.VAR}.{name}",
+                )
+            )
+
+    def _create_resource_vertices(self, file_path: Path, resources: dict[str, ResourceAttributes] | None) -> None:
+        if not resources:
+            return
+
+        for name, conf in resources.items():
+            config = deepcopy(conf)
+
+            attributes: dict[str, Any] = {}
+            attributes["decorators"] = deepcopy(config["decorators"])
+            attributes["type_"] = config["type"]
+            attributes["api_version_"] = config["api_version"]
+            attributes["existing_"] = config["existing"]
+            attributes.update(deepcopy(config["config"]))
+
+            attributes["resource_type"] = config["type"]
+            attributes["__start_line__"] = config["__start_line__"]
+            attributes["__end_line__"] = config["__end_line__"]
+
+            self.vertices.append(
+                BicepBlock(
+                    name=name,
+                    config=config,  # type:ignore[arg-type]
+                    path=str(file_path),
+                    block_type=BlockType.RESOURCE,
+                    attributes=attributes,
+                    id=f"{config['type']}.{name}",
+                )
+            )
+
+    def _create_output_vertices(self, file_path: Path, outputs: dict[str, OutputAttributes] | None) -> None:
+        if not outputs:
+            return
+
+        for name, conf in outputs.items():
+            config = deepcopy(conf)
+            attributes = deepcopy(conf)
+
+            self.vertices.append(
+                BicepBlock(
+                    name=name,
+                    config=config,  # type:ignore[arg-type]
+                    path=str(file_path),
+                    block_type=BlockType.OUTPUT,
+                    attributes=attributes,  # type:ignore[arg-type]
+                    id=f"{BlockType.OUTPUT}.{name}",
+                )
+            )
+
+    def _create_edges(self) -> None:
+        # TODO: support connections in interpolated strings
+        for origin_vertex_index, vertex in enumerate(self.vertices):
+            for attr_key, attr_value in vertex.attributes.items():
+                if isinstance(attr_value, BicepElement):
+                    self._create_edge(
+                        element_name=attr_value,
+                        origin_vertex_index=origin_vertex_index,
+                        label=attr_key,
+                    )
+                if isinstance(attr_value, list):
+                    for list_value in attr_value:
+                        if isinstance(list_value, BicepElement):
+                            self._create_edge(
+                                element_name=list_value,
+                                origin_vertex_index=origin_vertex_index,
+                                label=attr_key,
+                            )
+
+    def _create_edge(self, element_name: str, origin_vertex_index: int, label: str) -> None:
+        vertex_name = element_name
+        if "." in vertex_name:
+            # special case for`bicep elements, when properties are accessed
+            vertex_name = vertex_name.split(".")[0]
+
+        dest_vertex_index = self.vertices_by_name.get(vertex_name)
+        if dest_vertex_index:
+            if origin_vertex_index == dest_vertex_index:
+                return
+            edge = Edge(origin_vertex_index, dest_vertex_index, label)
+            self.edges.append(edge)
+            self.out_edges[origin_vertex_index].append(edge)
+            self.in_edges[dest_vertex_index].append(edge)
+
+    def update_vertices_configs(self) -> None:
+        pass
+
+    @staticmethod
+    def update_vertex_config(vertex: Block, changed_attributes: list[str] | dict[str, Any]) -> None:
+        pass
+
+    def get_resources_types_in_graph(self) -> list[str]:
+        pass
diff --git a/checkov/bicep/graph_manager.py b/checkov/bicep/graph_manager.py
new file mode 100644
index 0000000000..d91ef44e62
--- /dev/null
+++ b/checkov/bicep/graph_manager.py
@@ -0,0 +1,42 @@
+from __future__ import annotations
+
+from pathlib import Path
+from typing import Type, TYPE_CHECKING, Any
+
+from pycep.typing import BicepJson
+
+from checkov.bicep.parser import Parser
+from checkov.bicep.utils import get_scannable_file_paths
+from checkov.common.graph.db_connectors.db_connector import DBConnector
+from checkov.common.graph.graph_manager import GraphManager
+from checkov.bicep.graph_builder.local_graph import BicepLocalGraph
+
+if TYPE_CHECKING:
+    from checkov.common.graph.graph_builder.local_graph import LocalGraph
+
+
+class BicepGraphManager(GraphManager):
+    def __init__(self, db_connector: DBConnector, source: str = "Bicep") -> None:
+        super().__init__(db_connector=db_connector, parser=None, source=source)
+
+    def build_graph_from_source_directory(
+        self,
+        source_dir: str,
+        render_variables: bool = True,
+        local_graph_class: Type[LocalGraph] = BicepLocalGraph,
+        parsing_errors: dict[str, Exception] | None = None,
+        download_external_modules: bool = False,
+        excluded_paths: list[str] | None = None,
+    ) -> tuple[LocalGraph, dict[Path, BicepJson]]:
+        file_paths = get_scannable_file_paths(root_folder=source_dir)
+        definitions, definitions_raw, parsing_errors = Parser().get_files_definitions(file_paths)
+        local_graph = self.build_graph_from_definitions(definitions)
+
+        return local_graph, definitions
+
+    def build_graph_from_definitions(
+        self, definitions: dict[Path, BicepJson], render_variables: bool = True
+    ) -> LocalGraph:
+        local_graph = BicepLocalGraph(definitions)
+        local_graph.build_graph(False)
+        return local_graph
diff --git a/checkov/bicep/runner.py b/checkov/bicep/runner.py
index abb080c925..ed52ae6368 100644
--- a/checkov/bicep/runner.py
+++ b/checkov/bicep/runner.py
@@ -1,36 +1,65 @@
 from __future__ import annotations
 
-import re
+import logging
 from pathlib import Path
-from typing import cast
+from typing import cast, Type, TYPE_CHECKING
 
+from pycep.typing import BicepJson
 from typing_extensions import Literal
 
 from checkov.bicep.checks.param.registry import registry as param_registry
 from checkov.bicep.checks.resource.registry import registry as resource_registry
+from checkov.bicep.graph_builder.local_graph import BicepLocalGraph
+from checkov.bicep.graph_manager import BicepGraphManager
 from checkov.bicep.parser import Parser
-from checkov.common.checks.base_check_registry import BaseCheckRegistry
-from checkov.common.comment.enum import COMMENT_REGEX
-from checkov.common.models.enums import CheckResult
+from checkov.bicep.utils import clean_file_path, get_scannable_file_paths, search_for_suppression
+from checkov.common.checks_infra.registry import get_graph_checks_registry
+
+from checkov.common.graph.db_connectors.networkx.networkx_db_connector import NetworkxConnector
+from checkov.common.graph.graph_builder import CustomAttributes
+from checkov.common.output.graph_record import GraphRecord
 from checkov.common.output.record import Record
 from checkov.common.output.report import CheckType, Report
 from checkov.common.runners.base_runner import BaseRunner
 from checkov.common.typing import _CheckResult
 from checkov.runner_filter import RunnerFilter
 
+if TYPE_CHECKING:
+    from checkov.common.checks.base_check_registry import BaseCheckRegistry
+    from checkov.common.graph.checks_infra.registry import BaseRegistry
+    from checkov.common.graph.graph_builder.local_graph import LocalGraph
+    from checkov.common.graph.graph_manager import GraphManager
+
 
 class Runner(BaseRunner):
     check_type = CheckType.BICEP
-    bicep_cli = "bicep"
 
     block_type_registries: dict[Literal["parameters", "resources"], BaseCheckRegistry] = {
         "parameters": param_registry,
         "resources": resource_registry,
     }
 
+    def __init__(
+        self,
+        db_connector: NetworkxConnector = NetworkxConnector(),
+        source: str = "Bicep",
+        graph_class: Type[LocalGraph] = BicepLocalGraph,
+        graph_manager: GraphManager | None = None,
+        external_registries: list[BaseRegistry] | None = None,
+    ) -> None:
+        self.external_registries = external_registries if external_registries else []
+        self.graph_class = graph_class
+        self.graph_manager = (
+            graph_manager if graph_manager else BicepGraphManager(source=source, db_connector=db_connector)
+        )
+        self.graph_registry = get_graph_checks_registry(self.check_type)
+
+        self.definitions: dict[Path, BicepJson] = {}
+        self.definitions_raw: dict[Path, list[tuple[int, str]]] = {}
+
     def run(
         self,
-        root_folder: str | Path,
+        root_folder: str | Path | None,
         external_checks_dir: list[str] | None = None,
         files: list[str] | None = None,
         runner_filter: RunnerFilter = RunnerFilter(),
@@ -38,20 +67,39 @@ def run(
     ) -> Report:
         report = Report(Runner.check_type)
 
-        file_paths: set[Path] = set()
-        if root_folder:
-            root_path = Path(root_folder)
-            file_paths = {file_path for file_path in root_path.rglob("*.bicep")}
-        if files:
-            for file in files:
-                if file.endswith(".bicep"):
-                    file_paths.add(Path(file))
+        if self.context is None or self.definitions is None:
+            file_paths = get_scannable_file_paths(root_folder=root_folder, files=files)
+
+            self.definitions, self.definitions_raw, parsing_errors = Parser().get_files_definitions(file_paths)
+
+            report.add_parsing_errors(parsing_errors)
+
+            if external_checks_dir:
+                for directory in external_checks_dir:
+                    resource_registry.load_external_checks(directory)
+                    self.graph_registry.load_external_checks(directory)
+
+            self.context = None  # TODO: create context
+
+            logging.info("Creating Bicep graph")
+            local_graph = self.graph_manager.build_graph_from_definitions(self.definitions)
+            logging.info("Successfully created Bicep graph")
 
-        definitions, definitions_raw, parsing_errors = Parser().get_files_definitions(file_paths)
+            self.graph_manager.save_graph(local_graph)
+            self.definitions = local_graph.definitions  # TODO: adjust, when implementing variable rendering
 
-        report.add_parsing_errors(parsing_errors)
+        # run Python checks
+        self.add_python_check_results(report=report, runner_filter=runner_filter)
 
-        for file_path, definition in definitions.items():
+        # run YAML checks
+        self.add_yaml_check_results(report=report, runner_filter=runner_filter)
+
+        return report
+
+    def add_python_check_results(self, report: Report, runner_filter: RunnerFilter) -> None:
+        """Adds Python check results to given report"""
+
+        for file_path, definition in self.definitions.items():
             for block_type, registry in Runner.block_type_registries.items():
                 block_type_confs = definition.get(block_type)
                 if block_type_confs:
@@ -64,17 +112,15 @@ def run(
                         )
 
                         if results:
-                            file_code_lines = definitions_raw[file_path]
-                            start_line = cast(int, conf["__start_line__"])  # it is alwasy set for the main block types
-                            end_line = cast(int, conf["__end_line__"])  # it is alwasy set for the main block types
+                            file_code_lines = self.definitions_raw[file_path]
+                            start_line = cast(int, conf["__start_line__"])  # it is always set for the main block types
+                            end_line = cast(int, conf["__end_line__"])  # it is always set for the main block types
 
-                            cleaned_path = self.clean_file_path(file_path)
+                            cleaned_path = clean_file_path(file_path)
                             resource_id = f"{conf['type']}.{name}"
                             report.add_resource(f"{cleaned_path}:{resource_id}")
 
-                            suppressions = self.search_for_suppression(
-                                code_lines=file_code_lines[start_line - 1 : end_line]
-                            )
+                            suppressions = search_for_suppression(code_lines=file_code_lines[start_line - 1 : end_line])
 
                             for check, check_result in results.items():
                                 if check.id in suppressions.keys():
@@ -99,23 +145,42 @@ def run(
                                 record.set_guideline(check.guideline)
                                 report.add_record(record=record)
 
-        return report
-
-    def search_for_suppression(self, code_lines: list[tuple[int, str]]) -> dict[str, _CheckResult]:
-        suppressions = {}
+    def add_yaml_check_results(self, report: Report, runner_filter: RunnerFilter) -> None:
+        """Adds YAML check results to given report"""
 
-        for _, line in code_lines:
-            skip_search = re.search(COMMENT_REGEX, line)
-            if skip_search:
-                check_result: _CheckResult = {
-                    "result": CheckResult.SKIPPED,
-                    "suppress_comment": skip_search.group(3)[1:] if skip_search.group(3) else "No comment provided",
-                }
-                suppressions[skip_search.group(2)] = check_result
+        checks_results = self.run_graph_checks_results(runner_filter)
 
-        return suppressions
+        for check, check_results in checks_results.items():
+            for check_result in check_results:
+                entity = check_result["entity"]
+                entity_file_path = Path(entity.get(CustomAttributes.FILE_PATH))
 
-    def clean_file_path(self, file_path: Path) -> Path:
-        path_parts = [part for part in file_path.parts if part not in (".", "..")]
+                clean_check_result: _CheckResult = {
+                    "result": check_result["result"],
+                    "evaluated_keys": check_result["evaluated_keys"],
+                }
 
-        return Path(*path_parts)
+                file_code_lines = self.definitions_raw[entity_file_path]
+                start_line = entity["__start_line__"]
+                end_line = cast(int, entity["__end_line__"])
+
+                record = Record(
+                    check_id=check.id,
+                    bc_check_id=check.bc_id,
+                    check_name=check.name,
+                    check_result=clean_check_result,
+                    code_block=file_code_lines[start_line - 1 : end_line],
+                    file_path=str(clean_file_path(entity_file_path)),
+                    file_line_range=[start_line, end_line],
+                    resource=entity.get(CustomAttributes.ID),
+                    check_class=check.__class__.__module__,
+                    file_abs_path=str(entity_file_path.absolute()),
+                    evaluations=None,
+                    severity=check.bc_severity,
+                )
+                if self.breadcrumbs:
+                    breadcrumb = self.breadcrumbs.get(record.file_path, {}).get(record.resource)
+                    if breadcrumb:
+                        record = GraphRecord(record, breadcrumb)
+                record.set_guideline(check.guideline)
+                report.add_record(record=record)
diff --git a/checkov/bicep/utils.py b/checkov/bicep/utils.py
new file mode 100644
index 0000000000..09909649e5
--- /dev/null
+++ b/checkov/bicep/utils.py
@@ -0,0 +1,47 @@
+from __future__ import annotations
+
+import re
+from pathlib import Path
+
+from checkov.common.comment.enum import COMMENT_REGEX
+from checkov.common.models.enums import CheckResult
+from checkov.common.typing import _CheckResult
+
+
+def get_scannable_file_paths(root_folder: str | Path | None = None, files: list[str] | None = None) -> set[Path]:
+    """Finds Bicep files"""
+
+    file_paths: set[Path] = set()
+
+    if root_folder:
+        root_path = Path(root_folder)
+        file_paths = {file_path for file_path in root_path.rglob("*.bicep")}
+    if files:
+        for file in files:
+            if file.endswith(".bicep"):
+                file_paths.add(Path(file))
+
+    return file_paths
+
+
+def clean_file_path(file_path: Path) -> Path:
+    path_parts = [part for part in file_path.parts if part not in (".", "..")]
+
+    return Path(*path_parts)
+
+
+def search_for_suppression(code_lines: list[tuple[int, str]]) -> dict[str, _CheckResult]:
+    """Searches for suppressions in a config block"""
+
+    suppressions = {}
+
+    for _, line in code_lines:
+        skip_search = re.search(COMMENT_REGEX, line)
+        if skip_search:
+            check_result: _CheckResult = {
+                "result": CheckResult.SKIPPED,
+                "suppress_comment": skip_search.group(3)[1:] if skip_search.group(3) else "No comment provided",
+            }
+            suppressions[skip_search.group(2)] = check_result
+
+    return suppressions
diff --git a/checkov/common/checks_infra/registry.py b/checkov/common/checks_infra/registry.py
index 785afe8bed..33f4cdf76b 100644
--- a/checkov/common/checks_infra/registry.py
+++ b/checkov/common/checks_infra/registry.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import json
 import logging
 import os
@@ -60,10 +62,10 @@ def _get_resource_types(check_json: Dict[str, Dict[str, Any]]) -> Optional[List[
         return resources_types.get(provider)
 
 
-_registry_instances = {}
+_registry_instances: dict[str, Registry] = {}
 
 
-def get_graph_checks_registry(check_type):
+def get_graph_checks_registry(check_type: str) -> Registry:
     if not _registry_instances.get(check_type):
         _registry_instances[check_type] = Registry(parser=NXGraphCheckParser(),
                              checks_dir=f"{Path(__file__).parent.parent.parent}/{check_type}/checks/graph_checks")
diff --git a/checkov/common/graph/checks_infra/base_check.py b/checkov/common/graph/checks_infra/base_check.py
index 0049c4f449..5942ee151e 100644
--- a/checkov/common/graph/checks_infra/base_check.py
+++ b/checkov/common/graph/checks_infra/base_check.py
@@ -1,11 +1,14 @@
 import itertools
-from typing import Optional, Tuple, List, Dict, Any
+from typing import Optional, Tuple, List, Dict, Any, TYPE_CHECKING
 
 from networkx import DiGraph
 
 from checkov.common.graph.checks_infra.enums import SolverType
 from checkov.common.graph.checks_infra.solvers.base_solver import BaseSolver
 
+if TYPE_CHECKING:
+    from checkov.common.bridgecrew.severities import Severity
+
 
 class BaseGraphCheck:
     def __init__(self) -> None:
@@ -22,8 +25,8 @@ def __init__(self) -> None:
         self.type: Optional[SolverType] = None
         self.solver: Optional[BaseSolver] = None
         self.guideline: Optional[str] = None
-        self.benchmarks: Dict[str: list] = {}
-        self.bc_severity: Optional[str] = None
+        self.benchmarks: Dict[str, List[str]] = {}
+        self.bc_severity: Optional["Severity"] = None
         self.pc_severity: Optional[str] = None
         self.bc_category: Optional[str] = None
         self.pc_title: Optional[str] = None
diff --git a/checkov/common/graph/db_connectors/networkx/networkx_db_connector.py b/checkov/common/graph/db_connectors/networkx/networkx_db_connector.py
index 0110371689..41dcfa5fdb 100644
--- a/checkov/common/graph/db_connectors/networkx/networkx_db_connector.py
+++ b/checkov/common/graph/db_connectors/networkx/networkx_db_connector.py
@@ -1,23 +1,30 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
 import networkx as nx
 
 from checkov.common.graph.db_connectors.db_connector import DBConnector
 from checkov.common.graph.graph_builder import CustomAttributes
 
+if TYPE_CHECKING:
+    from checkov.common.graph.graph_builder.local_graph import LocalGraph
+
 
 class NetworkxConnector(DBConnector):
-    def __init__(self):
+    def __init__(self) -> None:
         self.graph = nx.DiGraph()
 
-    def save_graph(self, local_graph, add_bulk_edges=False):
+    def save_graph(self, local_graph: LocalGraph, add_bulk_edges: bool = False) -> nx.DiGraph:
         return self.networkx_from_local_graph(local_graph)
 
-    def get_reader_endpoint(self):
+    def get_reader_endpoint(self) -> nx.DiGraph:
         return self.graph
 
-    def get_writer_endpoint(self):
+    def get_writer_endpoint(self) -> nx.DiGraph:
         return self.graph
 
-    def networkx_from_local_graph(self, local_graph):
+    def networkx_from_local_graph(self, local_graph: LocalGraph) -> nx.DiGraph:
         self.graph = nx.DiGraph()
         vertices_attributes = [v.get_attribute_dict() for v in local_graph.vertices]
         vertices_to_add = [(attr[CustomAttributes.HASH], attr) for attr in vertices_attributes]
diff --git a/checkov/common/graph/graph_builder/graph_components/block_types.py b/checkov/common/graph/graph_builder/graph_components/block_types.py
index 8de5b28a07..ae57827321 100644
--- a/checkov/common/graph/graph_builder/graph_components/block_types.py
+++ b/checkov/common/graph/graph_builder/graph_components/block_types.py
@@ -1,9 +1,11 @@
 from dataclasses import dataclass
 
+from typing_extensions import Literal
+
 
 @dataclass
 class BlockType:
-    RESOURCE = "resource"
+    RESOURCE: Literal["resource"] = "resource"
 
-    def get(self, attr_name):
+    def get(self, attr_name: str) -> str:
         return getattr(self, attr_name.upper())
diff --git a/checkov/common/graph/graph_builder/graph_components/edge.py b/checkov/common/graph/graph_builder/graph_components/edge.py
index 650d124c63..1ac9f9d7da 100644
--- a/checkov/common/graph/graph_builder/graph_components/edge.py
+++ b/checkov/common/graph/graph_builder/graph_components/edge.py
@@ -1,3 +1,6 @@
+from typing import Any
+
+
 class Edge:
     def __init__(self, origin: int, dest: int, label: str) -> None:
         self.origin = origin
@@ -7,11 +10,11 @@ def __init__(self, origin: int, dest: int, label: str) -> None:
     def __str__(self) -> str:
         return f"[{self.origin} -({self.label})-> {self.dest}]"
 
-    def __eq__(self, other) -> bool:
+    def __eq__(self, other: Any) -> bool:
         return isinstance(other, Edge) and str(self) == str(other)
 
-    def __ne__(self, other) -> bool:
+    def __ne__(self, other: Any) -> bool:
         return not self.__eq__(other)
 
-    def __hash__(self):
+    def __hash__(self) -> int:
         return hash(str(self))
\ No newline at end of file
diff --git a/checkov/common/graph/graph_manager.py b/checkov/common/graph/graph_manager.py
index 502ddc68b3..dfbad94f7e 100644
--- a/checkov/common/graph/graph_manager.py
+++ b/checkov/common/graph/graph_manager.py
@@ -1,12 +1,19 @@
+from __future__ import annotations
+
 from abc import abstractmethod
-from typing import List, Type, Optional, Dict, Tuple, Any
+from pathlib import Path
+from typing import Type, Any, TYPE_CHECKING
 
 from checkov.common.graph.db_connectors.db_connector import DBConnector
 from checkov.common.graph.graph_builder.local_graph import LocalGraph
 
+if TYPE_CHECKING:
+    import networkx as nx
+    from checkov.terraform.parser import Parser
+
 
 class GraphManager:
-    def __init__(self, db_connector: DBConnector, parser, source: str = "") -> None:
+    def __init__(self, db_connector: DBConnector, parser: Parser | None, source: str = "") -> None:
         self.db_connector = db_connector
         self.source = source
         self.parser = parser
@@ -17,26 +24,26 @@ def build_graph_from_source_directory(
         source_dir: str,
         render_variables: bool = True,
         local_graph_class: Type[LocalGraph] = LocalGraph,
-        parsing_errors: Optional[Dict[str, Exception]] = None,
+        parsing_errors: dict[str, Exception] | None = None,
         download_external_modules: bool = False,
-        excluded_paths: Optional[List[str]] = None,
-    ) -> Tuple[LocalGraph, Dict[str, Dict[str, Any]]]:
+        excluded_paths: list[str] | None = None,
+    ) -> tuple[LocalGraph, dict[str, dict[str, Any]]]:
         pass
 
     @abstractmethod
     def build_graph_from_definitions(
-        self, definitions: Dict[str, Dict[str, Any]], render_variables: bool = True
+        self, definitions: dict[str | Path, dict[str, Any]], render_variables: bool = True
     ) -> LocalGraph:
         pass
 
-    def save_graph(self, graph):
+    def save_graph(self, graph: LocalGraph) -> nx.DiGraph:
         return self.db_connector.save_graph(graph)
 
-    def get_reader_endpoint(self):
+    def get_reader_endpoint(self) -> nx.DiGraph:
         return self.db_connector.get_reader_endpoint()
 
-    def get_writer_endpoint(self):
+    def get_writer_endpoint(self) -> nx.DiGraph:
         return self.db_connector.get_writer_endpoint()
 
-    def disconnect_from_db(self):
+    def disconnect_from_db(self) -> None:
         self.db_connector.disconnect()
diff --git a/checkov/common/runners/base_runner.py b/checkov/common/runners/base_runner.py
index 9892028ec6..a25548d35e 100644
--- a/checkov/common/runners/base_runner.py
+++ b/checkov/common/runners/base_runner.py
@@ -1,13 +1,20 @@
+from __future__ import annotations
+
 import itertools
 import os
 import re
 from abc import ABC, abstractmethod
-from typing import List, Dict, Optional, Any, Union
+from typing import List, Dict, Optional, Any, Union, TYPE_CHECKING
 
 from checkov.common.graph.checks_infra.base_check import BaseGraphCheck
 from checkov.common.output.report import Report
 from checkov.runner_filter import RunnerFilter
 
+if TYPE_CHECKING:
+    from checkov.common.checks_infra.registry import Registry
+    from checkov.common.graph.checks_infra.registry import BaseRegistry
+    from checkov.common.graph.graph_manager import GraphManager
+
 IGNORED_DIRECTORIES_ENV = os.getenv("CKV_IGNORED_DIRECTORIES", "node_modules,.terraform,.serverless")
 
 
@@ -37,9 +44,9 @@ class BaseRunner(ABC):
     definitions = None
     context = None
     breadcrumbs = None
-    external_registries = None
-    graph_manager = None
-    graph_registry = None
+    external_registries: list[BaseRegistry] | None = None
+    graph_manager: GraphManager | None = None
+    graph_registry: Registry | None = None
 
     @abstractmethod
     def run(
diff --git a/setup.py b/setup.py
index 8d53692e74..d0587e73f7 100644
--- a/setup.py
+++ b/setup.py
@@ -66,7 +66,7 @@
         "jsonpath_ng",
         "jsonschema~=3.0",
         "prettytable>=3.0.0",
-        "pycep-parser==0.3.2",
+        "pycep-parser==0.3.3",
         "charset-normalizer",
     ],
     license="Apache License 2.0",
diff --git a/tests/bicep/checks/resource/azure/example_SQLServerAuditingEnabled/main.bicep b/tests/bicep/checks/resource/azure/example_SQLServerAuditingEnabled/main.bicep
deleted file mode 100644
index fea25ee1a3..0000000000
--- a/tests/bicep/checks/resource/azure/example_SQLServerAuditingEnabled/main.bicep
+++ /dev/null
@@ -1,70 +0,0 @@
-// pass
-
-resource serverEnabled 'Microsoft.Sql/servers/auditingSettings@2021-02-01-preview' = {
-  name: 'default'
-  parent: sqlServer
-  properties: {
-    isAzureMonitorTargetEnabled: true
-    state: 'Enabled'
-  }
-}
-
-resource dbEnabled 'Microsoft.Sql/servers/databases/auditingSettings@2021-08-01-preview' = {
-  name: 'default'
-  parent: sqlServerDB
-  properties: {
-    isAzureMonitorTargetEnabled: true
-    state: 'Enabled'
-  }
-}
-
-resource nestedExample 'Microsoft.Sql/servers@2021-02-01-preview' = {
-  name: 'default'
-
-  properties: {
-    administratorLogin: sqlLogicalServer.userName
-    administratorLoginPassword: password
-    version: '12.0'
-    minimalTlsVersion: sqlLogicalServer.minimalTlsVersion
-    publicNetworkAccess: sqlLogicalServer.publicNetworkAccess
-  }
-
-  resource nestedAudit 'auditingSettings' = {
-    name: 'default123'
-
-    properties: {
-      isAzureMonitorTargetEnabled: true
-      state: 'Enabled'
-    }
-  }
-}
-
-// fail
-
-resource serverDefault 'Microsoft.Sql/servers/auditingSettings@2021-02-01-preview' = {
-  name: 'default'
-  parent: sqlServer
-}
-
-resource serverDisabled 'Microsoft.Sql/servers/auditingSettings@2021-02-01-preview' = {
-  name: 'default'
-  parent: sqlServer
-  properties: {
-    isAzureMonitorTargetEnabled: true
-    state: 'Disabled'
-  }
-}
-
-resource dbDefault 'Microsoft.Sql/servers/databases/auditingSettings@2021-08-01-preview' = {
-  name: 'default'
-  parent: sqlServerDB
-}
-
-resource dbDisabled 'Microsoft.Sql/servers/databases/auditingSettings@2021-08-01-preview' = {
-  name: 'default'
-  parent: sqlServerDB
-  properties: {
-    isAzureMonitorTargetEnabled: true
-    state: 'Disabled'
-  }
-}
diff --git a/tests/bicep/checks/resource/azure/test_SQLServerAuditingEnabled.py b/tests/bicep/checks/resource/azure/test_SQLServerAuditingEnabled.py
deleted file mode 100644
index 1e0fd0186a..0000000000
--- a/tests/bicep/checks/resource/azure/test_SQLServerAuditingEnabled.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from pathlib import Path
-
-from checkov.bicep.runner import Runner
-from checkov.bicep.checks.resource.azure.SQLServerAuditingEnabled import check
-from checkov.runner_filter import RunnerFilter
-
-
-def test_examples():
-    # given
-    test_files_dir = Path(__file__).parent / "example_SQLServerAuditingEnabled"
-
-    # when
-    report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id]))
-
-    # then
-    summary = report.get_summary()
-
-    passing_resources = {
-        "Microsoft.Sql/servers/auditingSettings.serverEnabled",
-        "Microsoft.Sql/servers/databases/auditingSettings.dbEnabled",
-        "Microsoft.Sql/servers/auditingSettings.nestedAudit",
-    }
-
-    failing_resources = {
-        "Microsoft.Sql/servers/auditingSettings.serverDefault",
-        "Microsoft.Sql/servers/auditingSettings.serverDisabled",
-        "Microsoft.Sql/servers/databases/auditingSettings.dbDefault",
-        "Microsoft.Sql/servers/databases/auditingSettings.dbDisabled",
-    }
-
-    passed_check_resources = {c.resource for c in report.passed_checks}
-    failed_check_resources = {c.resource for c in report.failed_checks}
-
-    assert summary["passed"] == 3
-    assert summary["failed"] == 4
-    assert summary["skipped"] == 0
-    assert summary["parsing_errors"] == 0
-
-    assert passed_check_resources == passing_resources
-    assert failed_check_resources == failing_resources
diff --git a/tests/bicep/graph/__init__.py b/tests/bicep/graph/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/bicep/graph/checks/__init__.py b/tests/bicep/graph/checks/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/bicep/graph/checks/resources/SQLServerAuditingEnabled/expected.yaml b/tests/bicep/graph/checks/resources/SQLServerAuditingEnabled/expected.yaml
new file mode 100644
index 0000000000..372653f9e4
--- /dev/null
+++ b/tests/bicep/graph/checks/resources/SQLServerAuditingEnabled/expected.yaml
@@ -0,0 +1,14 @@
+pass:
+  - 'Microsoft.Sql/servers.serverEnabled'
+  - 'Microsoft.Sql/servers.parentEnabled'
+  - 'Microsoft.Sql/servers/databases.databaseEnabled'
+fail:
+  - 'Microsoft.Sql/servers.serverDefault'
+  - 'Microsoft.Sql/servers.parentDefault'
+  - 'Microsoft.Sql/servers.serverDisabled'
+  - 'Microsoft.Sql/servers.parentDisabled'
+  - 'Microsoft.Sql/servers/databases.databaseDefault'
+  - 'Microsoft.Sql/servers/databases.databaseDisabled'
+evaluated_keys:
+  - 'resource_type'
+  - 'properties/state'
\ No newline at end of file
diff --git a/tests/bicep/graph/checks/resources/SQLServerAuditingEnabled/main.bicep b/tests/bicep/graph/checks/resources/SQLServerAuditingEnabled/main.bicep
new file mode 100644
index 0000000000..d44cf4a4f8
--- /dev/null
+++ b/tests/bicep/graph/checks/resources/SQLServerAuditingEnabled/main.bicep
@@ -0,0 +1,192 @@
+// pass
+
+resource serverEnabled 'Microsoft.Sql/servers@2021-02-01-preview' = {
+  name: 'default'
+  location: resourceGroup().location
+
+  properties: {
+    administratorLogin: sqlLogicalServer.userName
+    administratorLoginPassword: password
+    version: '12.0'
+    minimalTlsVersion: sqlLogicalServer.minimalTlsVersion
+    publicNetworkAccess: sqlLogicalServer.publicNetworkAccess
+  }
+
+  resource childEnabled 'auditingSettings' = {
+    name: 'default123'
+
+    properties: {
+      isAzureMonitorTargetEnabled: true
+      state: 'Enabled'
+    }
+  }
+}
+
+resource parentEnabled 'Microsoft.Sql/servers@2021-02-01-preview' = {
+  name: 'default'
+  location: resourceGroup().location
+
+  properties: {
+    administratorLogin: sqlLogicalServer.userName
+    administratorLoginPassword: password
+    version: '12.0'
+    minimalTlsVersion: sqlLogicalServer.minimalTlsVersion
+    publicNetworkAccess: sqlLogicalServer.publicNetworkAccess
+  }
+}
+
+resource childEnabled 'Microsoft.Sql/servers/auditingSettings@2021-02-01-preview' = {
+  name: 'default'
+
+  parent: parentEnabled
+
+  properties: {
+    isAzureMonitorTargetEnabled: true
+    state: 'Enabled'
+  }
+}
+
+resource databaseEnabled 'Microsoft.Sql/servers/databases@2020-08-01-preview' = {
+  name: '${server.name}/${sqlDBName}'
+  location: location
+  sku: {
+    name: 'Standard'
+    tier: 'Standard'
+  }
+
+  resource childEnabled 'auditingSettings' = {
+    name: 'default123'
+
+    properties: {
+      isAzureMonitorTargetEnabled: true
+      state: 'Enabled'
+    }
+  }
+}
+
+// fail
+
+resource serverDefault 'Microsoft.Sql/servers@2021-02-01-preview' = {
+  name: 'default'
+  location: resourceGroup().location
+
+  properties: {
+    administratorLogin: sqlLogicalServer.userName
+    administratorLoginPassword: password
+    version: '12.0'
+    minimalTlsVersion: sqlLogicalServer.minimalTlsVersion
+    publicNetworkAccess: sqlLogicalServer.publicNetworkAccess
+  }
+
+  resource childDefault 'auditingSettings' = {
+    name: 'default'
+
+    properties: {
+      isAzureMonitorTargetEnabled: true
+    }
+  }
+}
+
+resource parentDefault 'Microsoft.Sql/servers@2021-02-01-preview' = {
+  name: 'default'
+  location: resourceGroup().location
+
+  properties: {
+    administratorLogin: sqlLogicalServer.userName
+    administratorLoginPassword: password
+    version: '12.0'
+    minimalTlsVersion: sqlLogicalServer.minimalTlsVersion
+    publicNetworkAccess: sqlLogicalServer.publicNetworkAccess
+  }
+}
+
+resource childDefault 'Microsoft.Sql/servers/auditingSettings@2021-02-01-preview' = {
+  name: 'default'
+
+  parent: parentDefault
+
+  properties: {
+    isAzureMonitorTargetEnabled: true
+  }
+}
+
+resource databaseDefault 'Microsoft.Sql/servers/databases@2020-08-01-preview' = {
+  name: '${server.name}/${sqlDBName}'
+  location: location
+  sku: {
+    name: 'Standard'
+    tier: 'Standard'
+  }
+
+  resource childDefault 'auditingSettings' = {
+    name: 'default123'
+
+    properties: {
+      isAzureMonitorTargetEnabled: true
+    }
+  }
+}
+
+resource serverDisabled 'Microsoft.Sql/servers@2021-02-01-preview' = {
+  name: 'default'
+  location: resourceGroup().location
+
+  properties: {
+    administratorLogin: sqlLogicalServer.userName
+    administratorLoginPassword: password
+    version: '12.0'
+    minimalTlsVersion: sqlLogicalServer.minimalTlsVersion
+    publicNetworkAccess: sqlLogicalServer.publicNetworkAccess
+  }
+
+  resource childDisabled 'auditingSettings' = {
+    name: 'default'
+
+    properties: {
+      isAzureMonitorTargetEnabled: true
+      state: 'Disabled'
+    }
+  }
+}
+
+resource parentDisabled 'Microsoft.Sql/servers@2021-02-01-preview' = {
+  name: 'default'
+  location: resourceGroup().location
+
+  properties: {
+    administratorLogin: sqlLogicalServer.userName
+    administratorLoginPassword: password
+    version: '12.0'
+    minimalTlsVersion: sqlLogicalServer.minimalTlsVersion
+    publicNetworkAccess: sqlLogicalServer.publicNetworkAccess
+  }
+}
+
+resource childDisabled 'Microsoft.Sql/servers/auditingSettings@2021-02-01-preview' = {
+  name: 'default'
+
+  parent: parentDefault
+
+  properties: {
+    isAzureMonitorTargetEnabled: true
+    state: 'Disabled'
+  }
+}
+
+resource databaseDisabled 'Microsoft.Sql/servers/databases@2020-08-01-preview' = {
+  name: '${server.name}/${sqlDBName}'
+  location: location
+  sku: {
+    name: 'Standard'
+    tier: 'Standard'
+  }
+
+  resource childDisabled 'auditingSettings' = {
+    name: 'default123'
+
+    properties: {
+      isAzureMonitorTargetEnabled: true
+      state: 'Disabled'
+    }
+  }
+}
diff --git a/tests/bicep/graph/checks/resources/__init__.py b/tests/bicep/graph/checks/resources/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/bicep/graph/checks/test_yaml_policies.py b/tests/bicep/graph/checks/test_yaml_policies.py
new file mode 100644
index 0000000000..955389c0fd
--- /dev/null
+++ b/tests/bicep/graph/checks/test_yaml_policies.py
@@ -0,0 +1,67 @@
+import os
+import warnings
+from pathlib import Path
+from typing import List
+
+from checkov.bicep.graph_manager import BicepGraphManager
+from checkov.common.graph.db_connectors.networkx.networkx_db_connector import NetworkxConnector
+from checkov.common.graph.graph_builder import CustomAttributes
+from checkov.common.models.enums import CheckResult
+from checkov.common.output.record import Record
+from checkov.common.output.report import Report, CheckType
+from tests.common.graph.checks.test_yaml_policies_base import TestYamlPoliciesBase
+
+
+class TestYamlPolicies(TestYamlPoliciesBase):
+    def __init__(self, args):
+        graph_manager = BicepGraphManager(db_connector=NetworkxConnector())
+        super().__init__(
+            graph_manager=graph_manager,
+            real_graph_checks_path=str(
+                Path(__file__).parent.parent.parent.parent.parent / "checkov/bicep/checks/graph_checks"
+            ),
+            test_checks_path="",
+            check_type="bicep",
+            test_file_path=__file__,
+            args=args,
+        )
+
+    def setUp(self) -> None:
+        os.environ["UNIQUE_TAG"] = ""
+        warnings.filterwarnings("ignore", category=ResourceWarning)
+        warnings.filterwarnings("ignore", category=DeprecationWarning)
+
+    def test_SQLServerAuditingEnabled(self):
+        self.go("SQLServerAuditingEnabled")
+
+    def test_registry_load(self):
+        registry = self.get_checks_registry()
+        self.assertGreater(len(registry.checks), 0)
+
+    def assert_evaluated_keys(self, expected_evaluated_keys: List[str], results: List[Record]):
+        evaluated_keys_results = results[0].check_result["evaluated_keys"]
+        self.assertCountEqual(expected_evaluated_keys, evaluated_keys_results)
+
+    def create_report_from_graph_checks_results(self, checks_results, check):
+        report = Report(CheckType.BICEP)
+        first_results_key = list(checks_results.keys())[0]
+        for check_result in checks_results[first_results_key]:
+            entity = check_result["entity"]
+            record = Record(
+                check_id=check["id"],
+                check_name=check["name"],
+                check_result=check_result,
+                code_block=[(0, "")],
+                file_path=entity.get(CustomAttributes.FILE_PATH),
+                file_line_range=[entity.get("__startline__"), entity.get("__endline__")],
+                resource=f"{entity.get(CustomAttributes.RESOURCE_TYPE)}.{entity.get(CustomAttributes.BLOCK_NAME)}",
+                entity_tags=entity.get("tags", {}),
+                evaluations=None,
+                check_class="",
+                file_abs_path=entity.get(CustomAttributes.FILE_PATH),
+            )
+            if check_result["result"] == CheckResult.PASSED:
+                report.passed_checks.append(record)
+            if check_result["result"] == CheckResult.FAILED:
+                report.failed_checks.append(record)
+        return report
diff --git a/tests/bicep/graph/graph_builder/__init__.py b/tests/bicep/graph/graph_builder/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/bicep/graph/graph_builder/test_local_graph.py b/tests/bicep/graph/graph_builder/test_local_graph.py
new file mode 100644
index 0000000000..a903d6ba89
--- /dev/null
+++ b/tests/bicep/graph/graph_builder/test_local_graph.py
@@ -0,0 +1,70 @@
+from collections import Counter
+from pathlib import Path
+
+from checkov.bicep.graph_builder.graph_components.block_types import BlockType
+from checkov.bicep.graph_builder.local_graph import BicepLocalGraph
+from checkov.bicep.parser import Parser
+
+EXAMPLES_DIR = Path(__file__).parent.parent.parent / "examples"
+
+
+def test_build_graph():
+    # given
+    test_file = EXAMPLES_DIR / "playground.bicep"
+    template, _ = Parser().parse(test_file)
+    local_graph = BicepLocalGraph(definitions={test_file: template})
+
+    # when
+    local_graph.build_graph(render_variables=False)
+
+    # then
+    assert len(local_graph.vertices) == 24
+    assert len(local_graph.edges) == 29
+
+    assert len(local_graph.vertices_by_block_type[BlockType.TARGET_SCOPE]) == 1
+    assert len(local_graph.vertices_by_block_type[BlockType.PARAM]) == 5
+    assert len(local_graph.vertices_by_block_type[BlockType.VAR]) == 10
+    assert len(local_graph.vertices_by_block_type[BlockType.RESOURCE]) == 8
+    assert len(local_graph.vertices_by_block_type[BlockType.MODULE]) == 0
+    assert len(local_graph.vertices_by_block_type[BlockType.OUTPUT]) == 0
+
+    out_edge_counts = Counter([e.origin for e in local_graph.edges])
+    in_edge_counts = Counter([e.dest for e in local_graph.edges])
+
+    assert out_edge_counts == Counter(
+        {
+            16: 9,
+            19: 5,
+            20: 4,
+            17: 3,
+            18: 2,
+            21: 2,
+            22: 2,
+            23: 2,
+        }
+    )
+    assert in_edge_counts == Counter(
+        {
+            5: 8,
+            6: 2,
+            2: 1,
+            3: 1,
+            1: 1,
+            20: 1,
+            21: 1,
+            17: 1,
+            13: 1,
+            4: 1,
+            15: 1,
+            9: 1,
+            10: 1,
+            11: 1,
+            18: 1,
+            7: 1,
+            22: 1,
+            23: 1,
+            8: 1,
+            12: 1,
+            14: 1,
+        }
+    )
diff --git a/tests/bicep/test_graph_manager.py b/tests/bicep/test_graph_manager.py
new file mode 100644
index 0000000000..34719355ba
--- /dev/null
+++ b/tests/bicep/test_graph_manager.py
@@ -0,0 +1,53 @@
+from pathlib import Path
+
+from checkov.bicep.graph_manager import BicepGraphManager
+from checkov.bicep.parser import Parser
+from checkov.common.graph.db_connectors.networkx.networkx_db_connector import NetworkxConnector
+from checkov.common.graph.graph_builder.graph_components.block_types import BlockType
+
+EXAMPLES_DIR = Path(__file__).parent / "examples"
+
+
+def test_build_graph_from_source_directory():
+    # given
+    test_file = EXAMPLES_DIR / "playground.bicep"
+    graph_manager = BicepGraphManager(db_connector=NetworkxConnector())
+
+    # when
+    local_graph, definitions = graph_manager.build_graph_from_source_directory(source_dir=str(EXAMPLES_DIR))
+
+    # then
+    assert list(definitions.keys()) == [test_file]  # should no include 'malformed.bicep' file
+
+    assert len(local_graph.vertices) == 24
+    assert len(local_graph.edges) == 29
+
+    storage_account_idx = local_graph.vertices_by_name["diagsAccount"]  # vertices_by_name exists for BicepGraphManager
+    storage_account = local_graph.vertices[storage_account_idx]
+
+    assert storage_account.block_type == BlockType.RESOURCE
+    assert storage_account.id == "Microsoft.Storage/storageAccounts.diagsAccount"
+    assert storage_account.source == "Bicep"
+    assert storage_account.config == definitions[test_file]["resources"]["diagsAccount"]
+
+
+def test_build_graph_from_definitions():
+    # given
+    test_file = EXAMPLES_DIR / "playground.bicep"
+    graph_manager = BicepGraphManager(db_connector=NetworkxConnector())
+    template, _ = Parser().parse(test_file)
+
+    # when
+    local_graph = graph_manager.build_graph_from_definitions(definitions={test_file: template})
+
+    # then
+    assert len(local_graph.vertices) == 24
+    assert len(local_graph.edges) == 29
+
+    storage_account_idx = local_graph.vertices_by_name["diagsAccount"]  # vertices_by_name exists for BicepGraphManager
+    storage_account = local_graph.vertices[storage_account_idx]
+
+    assert storage_account.block_type == BlockType.RESOURCE
+    assert storage_account.id == "Microsoft.Storage/storageAccounts.diagsAccount"
+    assert storage_account.source == "Bicep"
+    assert storage_account.config == template["resources"]["diagsAccount"]
diff --git a/tests/common/graph/checks/test_yaml_policies_base.py b/tests/common/graph/checks/test_yaml_policies_base.py
index a360a0b5a7..9f05eb4800 100644
--- a/tests/common/graph/checks/test_yaml_policies_base.py
+++ b/tests/common/graph/checks/test_yaml_policies_base.py
@@ -1,3 +1,4 @@
+import itertools
 import json
 import os
 import yaml
@@ -28,9 +29,10 @@ def go(self, dir_name, check_name=None):
         dir_path = os.path.join(os.path.dirname(os.path.realpath(self.test_file_path)),
                                 f"resources/{dir_name}")
         assert os.path.exists(dir_path)
-        assert os.path.exists(self.checks_dir)
+        if self.checks_dir:
+            assert os.path.exists(self.checks_dir)
         found = False
-        for root, d_names, f_names in os.walk(self.checks_dir):
+        for root, d_names, f_names in itertools.chain(os.walk(self.real_graph_checks_path), os.walk(self.checks_dir)):
             for f_name in f_names:
                 check_name = dir_name if check_name is None else check_name
                 if f_name == f"{check_name}.yaml":
@@ -77,7 +79,8 @@ def get_policy_results(self, root_folder, policy):
     def get_checks_registry(self):
         registry = Registry(parser=NXGraphCheckParser(), checks_dir=self.real_graph_checks_path)
         registry.load_checks()
-        registry.load_external_checks(self.checks_dir)
+        if self.checks_dir:
+            registry.load_external_checks(self.checks_dir)
         return registry
 
     @abstractmethod


Code review comments:
diff_hunk: @@ -1,57 +1,105 @@
 from __future__ import annotations
 
-import re
+import logging
 from pathlib import Path
-from typing import cast
+from typing import cast, Type, TYPE_CHECKING
 
+from pycep.typing import BicepJson
 from typing_extensions import Literal
 
 from checkov.bicep.checks.param.registry import registry as param_registry
 from checkov.bicep.checks.resource.registry import registry as resource_registry
+from checkov.bicep.graph_builder.local_graph import BicepLocalGraph
+from checkov.bicep.graph_manager import BicepGraphManager
 from checkov.bicep.parser import Parser
-from checkov.common.checks.base_check_registry import BaseCheckRegistry
-from checkov.common.comment.enum import COMMENT_REGEX
-from checkov.common.models.enums import CheckResult
+from checkov.bicep.utils import clean_file_path, get_scannable_file_paths, search_for_suppression
+from checkov.common.checks_infra.registry import get_graph_checks_registry
+
+from checkov.common.graph.db_connectors.networkx.networkx_db_connector import NetworkxConnector
+from checkov.common.graph.graph_builder import CustomAttributes
+from checkov.common.output.graph_record import GraphRecord
 from checkov.common.output.record import Record
 from checkov.common.output.report import CheckType, Report
 from checkov.common.runners.base_runner import BaseRunner
 from checkov.common.typing import _CheckResult
 from checkov.runner_filter import RunnerFilter
 
+if TYPE_CHECKING:
+    from checkov.common.checks.base_check_registry import BaseCheckRegistry
+    from checkov.common.graph.checks_infra.registry import BaseRegistry
+    from checkov.common.graph.graph_builder.local_graph import LocalGraph
+    from checkov.common.graph.graph_manager import GraphManager
+
 
 class Runner(BaseRunner):
     check_type = CheckType.BICEP
-    bicep_cli = "bicep"
 
     block_type_registries: dict[Literal["parameters", "resources"], BaseCheckRegistry] = {
         "parameters": param_registry,
         "resources": resource_registry,
     }
 
+    def __init__(
+        self,
+        db_connector: NetworkxConnector = NetworkxConnector(),
+        source: str = "Bicep",
+        graph_class: Type[LocalGraph] = BicepLocalGraph,
+        graph_manager: GraphManager | None = None,
+        external_registries: list[BaseRegistry] | None = None,
+    ) -> None:
+        self.external_registries = external_registries if external_registries else []
+        self.graph_class = graph_class
+        self.graph_manager = (
+            graph_manager if graph_manager else BicepGraphManager(source=source, db_connector=db_connector)
+        )
+        self.graph_registry = get_graph_checks_registry(self.check_type)
+
+        self.definitions: dict[Path, BicepJson] = {}
+        self.definitions_raw: dict[Path, list[tuple[int, str]]] = {}
+
     def run(
         self,
-        root_folder: str | Path,
+        root_folder: str | Path | None,
         external_checks_dir: list[str] | None = None,
         files: list[str] | None = None,
         runner_filter: RunnerFilter = RunnerFilter(),
         collect_skip_comments: bool = True,
     ) -> Report:
         report = Report(Runner.check_type)
 
-        file_paths: set[Path] = set()
-        if root_folder:
-            root_path = Path(root_folder)
-            file_paths = {file_path for file_path in root_path.rglob("*.bicep")}
-        if files:
-            for file in files:
-                if file.endswith(".bicep"):
-                    file_paths.add(Path(file))
+        if self.context is None or self.definitions is None:
+            file_paths = get_scannable_file_paths(root_folder=root_folder, files=files)
+
+            self.definitions, self.definitions_raw, parsing_errors = Parser().get_files_definitions(file_paths)
+
+            report.add_parsing_errors(parsing_errors)
+
+            if external_checks_dir:
+                for directory in external_checks_dir:
+                    resource_registry.load_external_checks(directory)
+                    self.graph_registry.load_external_checks(directory)
+
+            self.context = None  # TODO: create context
+
+            logging.info("Creating Bicep graph")
+            local_graph = self.graph_manager.build_graph_from_definitions(self.definitions)
+            logging.info("Successfully created Bicep graph")
 
-        definitions, definitions_raw, parsing_errors = Parser().get_files_definitions(file_paths)
+            self.graph_manager.save_graph(local_graph)
+            self.definitions = local_graph.definitions  # TODO: adjust, when implementing variable rendering
 
-        report.add_parsing_errors(parsing_errors)
+        # run Python checks
+        self.add_python_check_results(report=report, runner_filter=runner_filter)
 
-        for file_path, definition in definitions.items():
+        # run YAML checks
+        self.add_yaml_check_results(report=report, runner_filter=runner_filter)
path: checkov/bicep/runner.py
text: @user1:
@author please call this graph checks, not YAML :)
They can be also described in JSON today, and possibly in other syntaxes in the future

@author:
yeah, good point. will do

diff_hunk: @@ -6,6 +6,8 @@
 from checkov.common.checks.base_check_registry import BaseCheckRegistry
 from checkov.runner_filter import RunnerFilter
 
+GRAPH_CHECK_IDS = ("CKV_AZURE_23",)
path: checkov/bicep/checks/resource/base_registry.py
text: @user1:
Can there be a different solution for getting the graph checks ids without hard coding them?

@author:
I was hesitant to create a real solution for this, because it is a special case, there is a Python ARM check with this number and I don't want it to be added to the bicep registry. But I will check, if I can come up with something nicer 🙂 

@user2:
Suggestion - when removing arm checks, also check the bicep graph registry :)

diff_hunk: @@ -0,0 +1,47 @@
+from __future__ import annotations
+
+import re
+from pathlib import Path
+
+from checkov.common.comment.enum import COMMENT_REGEX
+from checkov.common.models.enums import CheckResult
+from checkov.common.typing import _CheckResult
+
+
+def get_scannable_file_paths(root_folder: str | Path | None = None, files: list[str] | None = None) -> set[Path]:
+    """Finds Bicep files"""
+
+    file_paths: set[Path] = set()
+
+    if root_folder:
+        root_path = Path(root_folder)
+        file_paths = {file_path for file_path in root_path.rglob("*.bicep")}
+    if files:
+        for file in files:
+            if file.endswith(".bicep"):
+                file_paths.add(Path(file))
+
+    return file_paths
+
+
+def clean_file_path(file_path: Path) -> Path:
+    path_parts = [part for part in file_path.parts if part not in (".", "..")]
+
+    return Path(*path_parts)
+
+
+def search_for_suppression(code_lines: list[tuple[int, str]]) -> dict[str, _CheckResult]:
+    """Searches for suppressions in a config block"""
+
+    suppressions = {}
+
+    for _, line in code_lines:
+        skip_search = re.search(COMMENT_REGEX, line)
+        if skip_search:
+            check_result: _CheckResult = {
+                "result": CheckResult.SKIPPED,
+                "suppress_comment": skip_search.group(3)[1:] if skip_search.group(3) else "No comment provided",
+            }
+            suppressions[skip_search.group(2)] = check_result
+
+    return suppressions
path: checkov/bicep/utils.py
text: @user1:
@author Can we move to common util / abs parent and stop duplicating this logic?

@author:
yeah, sure, don't know why I didn't do it 😄