From b61956080ead4202c4b29b3800e1f1efdff9b48b Mon Sep 17 00:00:00 2001 From: dandds Date: Fri, 13 Dec 2019 16:32:46 -0500 Subject: [PATCH 1/3] Initial policies and method for creating policy definition. This adds some initial example policies: - One for region restrictions - One for service restrictions Note that the MS ARM team has said that region restrictions may be controlled by ARM, so that policy might prove unnecessary. The parameters list for the service restrictions is stubbed for now, pending the full list. I also added an internal method for adding policy definitions to a management group. This method is agnostic about what tier of management group the policy is being defined at. It requires that a dictionary representing the properties section of a valid Azure JSON policy definition be passed as an argument. --- Pipfile | 1 + Pipfile.lock | 213 +++++++++++------- atst/domain/csp/cloud.py | 47 ++++ atst/domain/csp/policy.py | 12 + .../portfolios/allowed-resource-types.json | 40 ++++ policies/portfolios/region-restriction.json | 51 +++++ tests/domain/cloud/test_azure_csp.py | 25 ++ tests/mock_azure.py | 7 + 8 files changed, 309 insertions(+), 87 deletions(-) create mode 100644 atst/domain/csp/policy.py create mode 100644 policies/portfolios/allowed-resource-types.json create mode 100644 policies/portfolios/region-restriction.json diff --git a/Pipfile b/Pipfile index edc8bbfc..b4591d6f 100644 --- a/Pipfile +++ b/Pipfile @@ -30,6 +30,7 @@ azure-graphrbac = "*" msrestazure = "*" azure-mgmt-authorization = "*" azure-mgmt-managementgroups = "*" +azure-mgmt-resource = "*" [dev-packages] bandit = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 80f6fc96..b97cfe92 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c2b19c436646705ea3bf4df8c35c2833083f048da37fc619e66f7236153607c5" + "sha256": "dc6d11d395668b3e1ae047bec87eac86848348a4d322e00fc9f9837a301a072c" }, "pipfile-spec": 6, "requires": { @@ -25,10 +25,10 @@ }, "alembic": { "hashes": [ - "sha256:49277bb7242192bbb9eac58fed4fe02ec6c3a2a4b4345d2171197459266482b2" + "sha256:3b0cb1948833e062f4048992fbc97ecfaaaac24aaa0d83a1202a99fb58af8c6d" ], "index": "pypi", - "version": "==1.3.1" + "version": "==1.3.2" }, "amqp": { "hashes": [ @@ -39,11 +39,11 @@ }, "apache-libcloud": { "hashes": [ - "sha256:9bc5cd5c32151bb7a04a7c7de0be9b4a4b8271e348ac91dd79eaaeeae627115f", - "sha256:fcc165f2cc2db9a379c6d3a17b3beb9081bb64ba5c0bf7bbb58da864810092f0" + "sha256:294d17774618dfb0896b14e69cdc3721850ec44dba81e7f91e701e75235a6eb1", + "sha256:29ee7d13b9b12d1335e752a489c01eed0c270940147f418cfff89ab66faf1305" ], "index": "pypi", - "version": "==2.6.1" + "version": "==2.7.0" }, "azure-common": { "hashes": [ @@ -76,6 +76,14 @@ "index": "pypi", "version": "==0.2.0" }, + "azure-mgmt-resource": { + "hashes": [ + "sha256:20b3394e4dc76fbd9459723cb8c0300fb18a8c32100076f023b5470426b9f104", + "sha256:eaea8b5d05495d1b74220052275d46b6bed93b59245bcaa747279a52e41c3bdf" + ], + "index": "pypi", + "version": "==7.0.0" + }, "azure-mgmt-subscription": { "hashes": [ "sha256:504b4c42ba859070c3c50637ec07ca36aca600e613fcccaa398db22822fe21f1", @@ -117,11 +125,11 @@ }, "celery": { "hashes": [ - "sha256:65f4d67fc1037edacecbf39fcf956da68b984cf2a6d89bd73a8a5a80e35e3dd7", - "sha256:8a59d80235b876881d9893751f2a87936165fc1347efee66095620b3cadf533b" + "sha256:7c544f37a84a5eadc44cab1aa8c9580dff94636bb81978cdf9bf8012d9ea7d8f", + "sha256:d3363bb5df72d74420986a435449f3c3979285941dff57d5d97ecba352a0e3e2" ], "index": "pypi", - "version": "==4.4.0rc4" + "version": "==4.4.0" }, "certifi": { "hashes": [ @@ -208,6 +216,15 @@ ], "version": "==2.8" }, + "enum34": { + "hashes": [ + "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850", + "sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a", + "sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79", + "sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1" + ], + "version": "==1.1.6" + }, "flask": { "hashes": [ "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", @@ -256,10 +273,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21", - "sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742" + "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", + "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" ], - "version": "==1.1.0" + "markers": "python_version < '3.8'", + "version": "==1.3.0" }, "isodate": { "hashes": [ @@ -284,10 +302,10 @@ }, "kombu": { "hashes": [ - "sha256:1760b54b1d15a547c9a26d3598a1c8cdaf2436386ac1f5561934bc8a3cbbbd86", - "sha256:e7465aa85a1db889116819f08c5de29520d2fa103324dcdca5e90af345f01771" + "sha256:2a9e7adff14d046c9996752b2c48b6d9185d0b992106d5160e1a179907a5d4ac", + "sha256:67b32ccb6fea030f8799f8fd50dd08e03a4b99464ebc4952d71d8747b1a52ad1" ], - "version": "==4.6.6" + "version": "==4.6.7" }, "lockfile": { "hashes": [ @@ -338,10 +356,10 @@ }, "more-itertools": { "hashes": [ - "sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", - "sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45" + "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", + "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" ], - "version": "==8.0.0" + "version": "==8.0.2" }, "msrest": { "hashes": [ @@ -514,10 +532,18 @@ }, "sqlalchemy": { "hashes": [ - "sha256:afa5541e9dea8ad0014251bc9d56171ca3d8b130c9627c6cb3681cff30be3f8a" + "sha256:bfb8f464a5000b567ac1d350b9090cf081180ec1ab4aa87e7bca12dab25320ec" ], "index": "pypi", - "version": "==1.3.11" + "version": "==1.3.12" + }, + "typing": { + "hashes": [ + "sha256:91dfe6f3f706ee8cc32d38edbbf304e9b7583fb37108fef38229617f8b3eba23", + "sha256:c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36", + "sha256:f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714" + ], + "version": "==3.7.4.1" }, "unipath": { "hashes": [ @@ -677,44 +703,46 @@ }, "colorama": { "hashes": [ - "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", - "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48" + "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", + "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" ], - "version": "==0.4.1" + "version": "==0.4.3" }, "coverage": { "hashes": [ - "sha256:2358e685d0253125da42a48396038d4c7b4cd1448c00bbc4bda0cb8c43c2a870", - "sha256:25017cf384eeed2e6caf72efd3ec4124e32a8b7a4387600499104387975400c7", - "sha256:2e2de9423ff8b14303a97eafddd16c479fbcc9a0b8b0be3b7c3843a3e0cf6d69", - "sha256:324ed908e4e40a6e2451056fe502470ad4e79495cb7a03ecab94e6309c3e117e", - "sha256:34f865a0cf6255b694a46e4383a7131c61ea72c5b4c4f81d20e522fb1e440b4b", - "sha256:3a2bcc464b60a18f1f7167b95b2773ede93bf3722bfa59e0802717f652b6cc25", - "sha256:48d70865266d649b6602e2ba94820d7972ef470d3b72a8fd41a3d17321feed3a", - "sha256:50cf23523ab3a724c6905d3b60f87fa8250d9bae3995e09f49f63effa2b54f15", - "sha256:54c84a68abd8c4c5b71878b35eb85321df41f3d144c78181867d5b026ec74994", - "sha256:5b59d661ee7f3200aedd7b71882b7927ea7ed522df75e3853f316a79ad872a2e", - "sha256:5ffb39624bc573177888a21fb301ccee46838c600b27d58c3e9dae495f44d34a", - "sha256:699b3072b7f0e69ed175a88fa8b2ec7eefc4f34d490c54ed9a52feff21a15fdc", - "sha256:79ef4a2bb862110bd585174e551a783bee5c3aa461734a2ac7429193be357589", - "sha256:8210a6f93c4a8c6d460b402e20e38399529b99200c3318542faf6a520c9b6a5c", - "sha256:8d30c10cfd0a6fdf0a2d5023de00ef7b329cd6ead2310c9e53eab79c209acb70", - "sha256:97ac79ff28f2cda6ac00a803ee582b965951755f61ab43377482bfba450b619a", - "sha256:9fe4aacacff9028ed167db108bf013510654f148d83c4857fed61d2ce0588bf2", - "sha256:a5b6395d5957d638f8b1870561607e3c39b1a236ea6cff9eafe5b9bb1db913f2", - "sha256:ab32c5fad6905986a7e34e3acf01180a69bb60c2aa7331815b46e51c776a1943", - "sha256:ad67f0cfdfecbd49b9da46a7e488e6dc32a69388740b85c36a4ef4b33082cbad", - "sha256:aedad67c30326a1af324f45833a40b97180664912deb29942459ddbe9fa0ce19", - "sha256:b077cd0e70f41366ac1f9d09275258fa1906758a5d4f31cacc18b10dfcf90784", - "sha256:b8ea210810d3c14aec7561f8fe0d3eec582d1088100aaa0bb8153d53d867d20f", - "sha256:bf572722326ce6704e863447a070039a827072b7179352570859be899b9e6551", - "sha256:c0df57e189dacd2606cae6386acf127d01d85b2bf49acd9a65543b5d6c359ddc", - "sha256:d523e75f2a8a0b4a6a8be1287c0e0e3a561b8832b05ddd987d4cd7c62f3ad3bc", - "sha256:e10593c60c5f0bfd8b241bf9f27ef2191a3005b73dde8ada0424f642543a1e59", - "sha256:e9128444c83bc260aea988bf1ca6278a33ba730955bf94720468c656b61353eb", - "sha256:f7162f2e3711f3a08a8a741f92e1f63afd58d0713177979f2cf9723dd50161cf" + "sha256:0cd13a6e98c37b510a2d34c8281d5e1a226aaf9b65b7d770ef03c63169965351", + "sha256:1a4b6b6a2a3a6612e6361130c2cc3dc4378d8c221752b96167ccbad94b47f3cd", + "sha256:2ee55e6dba516ddf6f484aa83ccabbb0adf45a18892204c23486938d12258cde", + "sha256:3be5338a2eb4ef03c57f20917e1d12a1fd10e3853fed060b6d6b677cb3745898", + "sha256:44b783b02db03c4777d8cf71bae19eadc171a6f2a96777d916b2c30a1eb3d070", + "sha256:475bf7c4252af0a56e1abba9606f1e54127cdf122063095c75ab04f6f99cf45e", + "sha256:47c81ee687eafc2f1db7f03fbe99aab81330565ebc62fb3b61edfc2216a550c8", + "sha256:4a7f8e72b18f2aca288ff02255ce32cc830bc04d993efbc87abf6beddc9e56c0", + "sha256:50197163a22fd17f79086e087a787883b3ec9280a509807daf158dfc2a7ded02", + "sha256:56b13000acf891f700f5067512b804d1ec8c301d627486c678b903859d07f798", + "sha256:79388ae29c896299b3567965dbcd93255f175c17c6c7bca38614d12718c47466", + "sha256:79fd5d3d62238c4f583b75d48d53cdae759fe04d4fb18fe8b371d88ad2b6f8be", + "sha256:7fe3e2fde2bf1d7ce25ebcd2d3de3650b8d60d9a73ce6dcef36e20191291613d", + "sha256:81042a24f67b96e4287774014fa27220d8a4d91af1043389e4d73892efc89ac6", + "sha256:81326f1095c53111f8afc95da281e1414185f4a538609a77ca50bdfa39a6c207", + "sha256:8873dc0d8f42142ea9f20c27bbdc485190fff93823c6795be661703369e5877d", + "sha256:88d2cbcb0a112f47eef71eb95460b6995da18e6f8ca50c264585abc2c473154b", + "sha256:91f2491aeab9599956c45a77c5666d323efdec790bfe23fcceafcd91105d585a", + "sha256:979daa8655ae5a51e8e7a24e7d34e250ae8309fd9719490df92cbb2fe2b0422b", + "sha256:9c871b006c878a890c6e44a5b2f3c6291335324b298c904dc0402ee92ee1f0be", + "sha256:a6d092545e5af53e960465f652e00efbf5357adad177b2630d63978d85e46a72", + "sha256:b5ed7837b923d1d71c4f587ae1539ccd96bfd6be9788f507dbe94dab5febbb5d", + "sha256:ba259f68250f16d2444cbbfaddaa0bb20e1560a4fdaad50bece25c199e6af864", + "sha256:be1d89614c6b6c36d7578496dc8625123bda2ff44f224cf8b1c45b810ee7383f", + "sha256:c1b030a79749aa8d1f1486885040114ee56933b15ccfc90049ba266e4aa2139f", + "sha256:c95bb147fab76f2ecde332d972d8f4138b8f2daee6c466af4ff3b4f29bd4c19e", + "sha256:d52c1c2d7e856cecc05aa0526453cb14574f821b7f413cc279b9514750d795c1", + "sha256:d609a6d564ad3d327e9509846c2c47f170456344521462b469e5cb39e48ba31c", + "sha256:e1bad043c12fb58e8c7d92b3d7f2f49977dcb80a08a6d1e7a5114a11bf819fca", + "sha256:e5a675f6829c53c87d79117a8eb656cc4a5f8918185a32fc93ba09778e90f6db", + "sha256:fec32646b98baf4a22fdceb08703965bd16dea09051fbeb31a04b5b6e72b846c" ], - "version": "==5.0b1" + "version": "==5.0" }, "decorator": { "hashes": [ @@ -747,10 +775,10 @@ }, "faker": { "hashes": [ - "sha256:48c03580720e0b46538d528b1296e4e5b24a809dcaf33a7dddec719489a9edb8", - "sha256:6327c665c0d8721280b3036d9c9e851c60092bc1f30c8394cc433f8723e2bda5" + "sha256:202ad3b2ec16ae7c51c02904fb838831f8d2899e61bf18db1e91a5a582feab11", + "sha256:92c84a10bec81217d9cb554ee12b3838c8986ce0b5d45f72f769da22e4bb5432" ], - "version": "==2.0.4" + "version": "==3.0.0" }, "flask": { "hashes": [ @@ -791,25 +819,26 @@ }, "importlib-metadata": { "hashes": [ - "sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21", - "sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742" + "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", + "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" ], - "version": "==1.1.0" + "markers": "python_version < '3.8'", + "version": "==1.3.0" }, "ipdb": { "hashes": [ - "sha256:473fdd798a099765f093231a8b1fabfa95b0b682fce12de0c74b61a4b4d8ee57" + "sha256:5d9a4a0e3b7027a158fc6f2929934341045b9c3b0b86ed5d7e84e409653f72fd" ], "index": "pypi", - "version": "==0.12.2" + "version": "==0.12.3" }, "ipython": { "hashes": [ - "sha256:c66c7e27239855828a764b1e8fc72c24a6f4498a2637572094a78c5551fb9d51", - "sha256:f186b01b36609e0c5d0de27c7ef8e80c990c70478f8c880863004b3489a9030e" + "sha256:190a279bd3d4fc585a611e9358a88f1048cc57fd688254a86f9461889ee152a6", + "sha256:762d79a62b6aa96b04971e920543f558dfbeedc0468b899303c080c8068d4ac2" ], "index": "pypi", - "version": "==7.10.1" + "version": "==7.10.2" }, "ipython-genutils": { "hashes": [ @@ -914,10 +943,10 @@ }, "more-itertools": { "hashes": [ - "sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", - "sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45" + "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", + "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" ], - "version": "==8.0.0" + "version": "==8.0.2" }, "mypy": { "hashes": [ @@ -948,10 +977,10 @@ }, "parso": { "hashes": [ - "sha256:63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", - "sha256:666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c" + "sha256:55cf25df1a35fd88b878715874d2c4dc1ad3f0eebd1e0266a67e1f55efccfbe1", + "sha256:5c1f7791de6bd5dbbeac8db0ef5594b36799de198b3f7f7014643b0c5536b9d3" ], - "version": "==0.5.1" + "version": "==0.5.2" }, "pathspec": { "hashes": [ @@ -1063,11 +1092,11 @@ }, "pytest-mock": { "hashes": [ - "sha256:96a0cebc66e09930be2a15b03333d90b59584d3fb011924f81c14b50ee0afbba", - "sha256:e5381be2608e49547f5e47633c5f81241ebf6206d17ce516a7a18d5a917e3859" + "sha256:67e414b3caef7bff6fc6bd83b22b5bc39147e4493f483c2679bc9d4dc485a94d", + "sha256:e24a911ec96773022ebcc7030059b57cd3480b56d4f5d19b7c370ec635e6aed5" ], "index": "pypi", - "version": "==1.12.1" + "version": "==1.13.0" }, "pytest-watch": { "hashes": [ @@ -1102,21 +1131,31 @@ }, "regex": { "hashes": [ - "sha256:15454b37c5a278f46f7aa2d9339bda450c300617ca2fca6558d05d870245edc7", - "sha256:1ad40708c255943a227e778b022c6497c129ad614bb7a2a2f916e12e8a359ee7", - "sha256:5e00f65cc507d13ab4dfa92c1232d004fa202c1d43a32a13940ab8a5afe2fb96", - "sha256:604dc563a02a74d70ae1f55208ddc9bfb6d9f470f6d1a5054c4bd5ae58744ab1", - "sha256:720e34a539a76a1fedcebe4397290604cc2bdf6f81eca44adb9fb2ea071c0c69", - "sha256:7caf47e4a9ac6ef08cabd3442cc4ca3386db141fb3c8b2a7e202d0470028e910", - "sha256:7faf534c1841c09d8fefa60ccde7b9903c9b528853ecf41628689793290ca143", - "sha256:b4e0406d822aa4993ac45072a584d57aa4931cf8288b5455bbf30c1d59dbad59", - "sha256:c31eaf28c6fe75ea329add0022efeed249e37861c19681960f99bbc7db981fb2", - "sha256:c7393597191fc2043c744db021643549061e12abe0b3ff5c429d806de7b93b66", - "sha256:d2b302f8cdd82c8f48e9de749d1d17f85ce9a0f082880b9a4859f66b07037dc6", - "sha256:e3d8dd0ec0ea280cf89026b0898971f5750a7bd92cb62c51af5a52abd020054a", - "sha256:ec032cbfed59bd5a4b8eab943c310acfaaa81394e14f44454ad5c9eba4f24a74" + "sha256:3d2d5952639615815a5dde7431265fc45b58106ca687ea16b60699c6946bfe02", + "sha256:3dbd8333fd2ebd50977ac8747385a73aa1f546eb6b16fcd83d274470fe11f243", + "sha256:40b7d1291a56897927e08bb973f8c186c2feb14c7f708bfe7aaee09483e85a20", + "sha256:517a827c4c9bcd4a158f48970258c17a52bdd133300c93affe8b5e9ae7b4675d", + "sha256:580e849ba64d7d82019c52d766b1f42cd8a398622278e8f89126800d7e5d52ea", + "sha256:719978a9145d59fc78509ea1d1bb74243f93583ef2a34dcc5623cf8118ae9726", + "sha256:75cf3796f89f75f83207a5c6a6e14eaf57e0369ef0ffff8e22bf36bbcfa0f1de", + "sha256:77396cf80be8b2a35db863cca4c1a902d88ceeb183adab328b81184e71a5eafe", + "sha256:77a3799152951d6d14ae5720ca162c97c64f85d4755da585418eac216b736cad", + "sha256:91235c98283d2bddf1a588f0fbc2da8afa37959294bbd18b76297bdf316ba4d6", + "sha256:99981ccc110b55d3bc2f5140167b8515c57f782ccdd0cd85d8217e1af1f1c632", + "sha256:a07f449fef6b8159080fd0be0e15b593ea55c2760f6340076fecc2845eac6c42", + "sha256:a35d1353ec805cea8a74bc5e141cc20083f2ade3d68bd92aa8b54115f08b902b", + "sha256:a70eb00ea3c18f68102cb125677812f66cacecc5a6187fa48eaef898b3ba45e5", + "sha256:aaffd68c4c1ed891366d5c390081f4bf6337595e76a157baf453603d8e53fbcb", + "sha256:ad9e3c7260809c0d1ded100269f78ea0217c0704f1eaaf40a382008461848b45", + "sha256:ba442ecf7df4c1657db237fa284719d95a8298bbac0056a73aca4e8a127f2968", + "sha256:bbddddfc768052abe88d02010c1f00c7dcd88430b775239fcc44ad2b1c9285b1", + "sha256:c203c9ee755e9656d0af8fab82754d5a664ebaf707b3f883c7eff6a3dd5151cf", + "sha256:c630f23491d0810767d3523b18b9c1e67c35319e298ed7e1a05dea0402ebd424", + "sha256:ccb78a4bf24b150e5b62cf5edff1e5f0b700064407dd0f03a5db7ff8451ee437", + "sha256:e865bc508e316a3a09d36c8621596e6599a203bc54f1cd41020a127ccdac468a", + "sha256:f8e3ca4669f6b09fdcbb3da71b358c7976f203d3e86b9e1be47790ed8915085e" ], - "version": "==2019.11.1" + "version": "==2019.12.9" }, "requests": { "hashes": [ diff --git a/atst/domain/csp/cloud.py b/atst/domain/csp/cloud.py index ea48ba4e..adb2260f 100644 --- a/atst/domain/csp/cloud.py +++ b/atst/domain/csp/cloud.py @@ -401,6 +401,7 @@ REMOTE_ROOT_ROLE_DEF_ID = "/providers/Microsoft.Authorization/roleDefinitions/00 class AzureSDKProvider(object): def __init__(self): from azure.mgmt import subscription, authorization, managementgroups + from azure.mgmt.resource import policy import azure.graphrbac as graphrbac import azure.common.credentials as credentials from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD @@ -410,6 +411,7 @@ class AzureSDKProvider(object): self.managementgroups = managementgroups self.graphrbac = graphrbac self.credentials = credentials + self.policy = policy # may change to a JEDI cloud self.cloud = AZURE_PUBLIC_CLOUD @@ -561,6 +563,51 @@ class AzureCloudProvider(CloudProviderInterface): # but we just don't have a valid ID pass + AZURE_MANAGEMENT_API = "https://management.azure.com" + + def _create_policy_definition( + self, credentials, subscription_id, management_group_id, properties, + ): + """ + Requires credentials that have AZURE_MANAGEMENT_API + specified as the resource. The Service Principal + specified in the credentials must have the "Resource + Policy Contributor" role assigned with a scope at least + as high as the management group specified by + management_group_id. + + Arguments: + credentials -- ServicePrincipalCredentials + subscription_id -- str, ID of the subscription (just the UUID, not the path) + management_group_id -- str, ID of the management group (just the UUID, not the path) + properties -- dictionary, the "properties" section of a valid Azure policy definition document + + Returns: + azure.mgmt.resource.policy.[api version].models.PolicyDefinition: the PolicyDefinition object provided to Azure + + Raises: + TBD + """ + # TODO: which subscription would this be? + client = self.sdk.policy.PolicyClient(credentials, subscription_id) + + definition = client.policy_definitions.models.PolicyDefinition( + policy_type=properties.get("policyType"), + mode=properties.get("mode"), + display_name=properties.get("displayName"), + description=properties.get("description"), + policy_rule=properties.get("policyRule"), + parameters=properties.get("parameters"), + ) + + name = properties.get("displayName") + + return client.policy_definitions.create_or_update_at_management_group( + policy_definition_name=name, + parameters=definition, + management_group_id=management_group_id, + ) + def _get_management_service_principal(self): # we really should be using graph.microsoft.com, but i'm getting # "expired token" errors for that diff --git a/atst/domain/csp/policy.py b/atst/domain/csp/policy.py new file mode 100644 index 00000000..43b4e232 --- /dev/null +++ b/atst/domain/csp/policy.py @@ -0,0 +1,12 @@ +class AzurePolicyManager: + def __init__(self, static_policy_location): + self._static_policy_location = static_policy_location + + def portfolio_definitions(): + pass + + def application_definitions(): + pass + + def environment_definitions(): + pass diff --git a/policies/portfolios/allowed-resource-types.json b/policies/portfolios/allowed-resource-types.json new file mode 100644 index 00000000..7e4dcb7f --- /dev/null +++ b/policies/portfolios/allowed-resource-types.json @@ -0,0 +1,40 @@ +{ + "definitionPoint": "portfolio-parent", + "policyDefinition": { + "properties": { + "displayName": "Allowed resource types", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enables you to specify the resource types that your organization can deploy.", + "parameters": { + "listOfResourceTypesAllowed": { + "type": "Array", + "metadata": { + "description": "The list of resource types that can be deployed.", + "displayName": "Allowed resource types", + "strongType": "resourceTypes" + } + } + }, + "policyRule": { + "if": { + "not": { + "field": "type", + "in": "[parameters('listOfResourceTypesAllowed')]" + } + }, + "then": { + "effect": "deny" + } + } + }, + "type": "Microsoft.Authorization/policyDefinitions" + }, + "parameters": { + "listOfResourceTypesAllowed": { + "value": [ + "Microsoft.Cache" + ] + } + } +} diff --git a/policies/portfolios/region-restriction.json b/policies/portfolios/region-restriction.json new file mode 100644 index 00000000..9a7f57ba --- /dev/null +++ b/policies/portfolios/region-restriction.json @@ -0,0 +1,51 @@ +{ + "definitionPoint": "portfolio-parent", + "policyDefinition": { + "properties": { + "displayName": "Custom - Region Restriction", + "policyType": "Custom", + "mode": "Indexed", + "parameters": { + "listOfAllowedLocations": { + "type": "Array", + "metadata": { + "displayName": "Allowed locations", + "description": "The list of locations that can be specified when deploying resources.", + "strongType": "location" + } + } + }, + "policyRule": { + "if": { + "allOf": [ + { + "field": "location", + "notIn": "[parameters('listOfAllowedLocations')]" + }, + { + "field": "location", + "notEquals": "global" + }, + { + "field": "type", + "notEquals": "Microsoft.AzureActiveDirectory/b2cDirectories" + } + ] + }, + "then": { + "effect": "Deny" + } + } + }, + "type": "Microsoft.Authorization/policyDefinitions" + }, + "parameters": { + "listOfAllowedLocations": { + "value": [ + "eastus", + "southcentralus", + "westus" + ] + } + } +} diff --git a/tests/domain/cloud/test_azure_csp.py b/tests/domain/cloud/test_azure_csp.py index 39c6655e..dc10a6df 100644 --- a/tests/domain/cloud/test_azure_csp.py +++ b/tests/domain/cloud/test_azure_csp.py @@ -90,3 +90,28 @@ def test_create_atat_admin_user_succeeds(mock_azure: AzureCloudProvider): result = mock_azure.create_atat_admin_user(AUTH_CREDENTIALS, environment_id) assert result.get("csp_user_id") == csp_user_id + + +def test_create_policy_definition_succeeds(mock_azure: AzureCloudProvider): + subscription_id = str(uuid4()) + management_group_id = str(uuid4()) + properties = { + "policyType": "test", + "displayName": "test policy", + } + + result = mock_azure._create_policy_definition( + AUTH_CREDENTIALS, subscription_id, management_group_id, properties + ) + azure_sdk_method = ( + mock_azure.sdk.policy.PolicyClient.return_value.policy_definitions.create_or_update_at_management_group + ) + mock_policy_definition = ( + mock_azure.sdk.policy.PolicyClient.return_value.policy_definitions.models.PolicyDefinition() + ) + assert azure_sdk_method.called + azure_sdk_method.assert_called_with( + management_group_id=management_group_id, + policy_definition_name=properties.get("displayName"), + parameters=mock_policy_definition, + ) diff --git a/tests/mock_azure.py b/tests/mock_azure.py index a360df64..b8391811 100644 --- a/tests/mock_azure.py +++ b/tests/mock_azure.py @@ -46,6 +46,12 @@ def mock_credentials(): return Mock(spec=credentials) +def mock_policy(): + from azure.mgmt.resource import policy + + return Mock(spec=policy) + + class MockAzureSDK(object): def __init__(self): from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD @@ -55,6 +61,7 @@ class MockAzureSDK(object): self.managementgroups = mock_managementgroups() self.graphrbac = mock_graphrbac() self.credentials = mock_credentials() + self.policy = mock_policy() # may change to a JEDI cloud self.cloud = AZURE_PUBLIC_CLOUD From 6460c912dd64a72817b0f31b693220f90beba636 Mon Sep 17 00:00:00 2001 From: dandds Date: Wed, 18 Dec 2019 12:19:50 -0500 Subject: [PATCH 2/3] Remove Libcloud dependency --- Pipfile | 1 - Pipfile.lock | 102 +++++++++++++++++++-------------------------------- 2 files changed, 37 insertions(+), 66 deletions(-) diff --git a/Pipfile b/Pipfile index b4591d6f..ed87d000 100644 --- a/Pipfile +++ b/Pipfile @@ -18,7 +18,6 @@ flask-session = "*" flask-wtf = "*" pyopenssl = "*" requests = "*" -apache-libcloud = "*" lockfile = "*" werkzeug = "*" PyYAML = "*" diff --git a/Pipfile.lock b/Pipfile.lock index b97cfe92..d30bcbb2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "dc6d11d395668b3e1ae047bec87eac86848348a4d322e00fc9f9837a301a072c" + "sha256": "c203c47b00f413fd40056ef6d2d8e51b37ad3ff5f7693db5eb170b7f8fd43234" }, "pipfile-spec": 6, "requires": { @@ -37,14 +37,6 @@ ], "version": "==2.5.2" }, - "apache-libcloud": { - "hashes": [ - "sha256:294d17774618dfb0896b14e69cdc3721850ec44dba81e7f91e701e75235a6eb1", - "sha256:29ee7d13b9b12d1335e752a489c01eed0c270940147f418cfff89ab66faf1305" - ], - "index": "pypi", - "version": "==2.7.0" - }, "azure-common": { "hashes": [ "sha256:53b1195b8f20943ccc0e71a17849258f7781bc6db1c72edc7d6c055f79bd54e3", @@ -216,15 +208,6 @@ ], "version": "==2.8" }, - "enum34": { - "hashes": [ - "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850", - "sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a", - "sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79", - "sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1" - ], - "version": "==1.1.6" - }, "flask": { "hashes": [ "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", @@ -537,14 +520,6 @@ "index": "pypi", "version": "==1.3.12" }, - "typing": { - "hashes": [ - "sha256:91dfe6f3f706ee8cc32d38edbbf304e9b7583fb37108fef38229617f8b3eba23", - "sha256:c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36", - "sha256:f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714" - ], - "version": "==3.7.4.1" - }, "unipath": { "hashes": [ "sha256:09839adcc72e8a24d4f76d63656f30b5a1f721fc40c9bcd79d8c67bdd8b47dae", @@ -950,23 +925,22 @@ }, "mypy": { "hashes": [ - "sha256:02d9bdd3398b636723ecb6c5cfe9773025a9ab7f34612c1cde5c7f2292e2d768", - "sha256:088f758a50af31cf8b42688118077292370c90c89232c783ba7979f39ea16646", - "sha256:28e9fbc96d13397a7ddb7fad7b14f373f91b5cff538e0772e77c270468df083c", - "sha256:30e123b24931f02c5d99307406658ac8f9cd6746f0d45a3dcac2fe5fbdd60939", - "sha256:3294821b5840d51a3cd7a2bb63b40fc3f901f6a3cfb3c6046570749c4c7ef279", - "sha256:41696a7d912ce16fdc7c141d87e8db5144d4be664a0c699a2b417d393994b0c2", - "sha256:4f42675fa278f3913340bb8c3371d191319704437758d7c4a8440346c293ecb2", - "sha256:54d205ccce6ed930a8a2ccf48404896d456e8b87812e491cb907a355b1a9c640", - "sha256:6992133c95a2847d309b4b0c899d7054adc60481df6f6b52bb7dee3d5fd157f7", - "sha256:6ecbd0e8e371333027abca0922b0c2c632a5b4739a0c61ffbd0733391e39144c", - "sha256:83fa87f556e60782c0fc3df1b37b7b4a840314ba1ac27f3e1a1e10cb37c89c17", - "sha256:c87ac7233c629f305602f563db07f5221950fe34fe30af072ac838fa85395f78", - "sha256:de9ec8dba773b78c49e7bec9a35c9b6fc5235682ad1fc2105752ae7c22f4b931", - "sha256:f385a0accf353ca1bca4bbf473b9d83ed18d923fdb809d3a70a385da23e25b6a" + "sha256:0308c35fd16c96a81b8dfc4d09ec63b8fa607cfec087acf5aafb44c2c45197de", + "sha256:39f7be2f89668d21b2bbab45ce5aa15e69bf8d6f3b46f9e1cc1a88e4fcc84f3d", + "sha256:4223f576813c79a10d0fd14192c86f1b85e3bd235c93792f22ed811a20b5ee4e", + "sha256:4c8f812a2fbefa96185933fbe05aa035e9cf791cf3a23bbdb6a219c80b60e0b1", + "sha256:4ea9ee847ea5bb38ea275441f3aea7eeba1b96187a3f968ee359d33d9dcc0eda", + "sha256:573c68df69f0e399fa57866a0b72989acf0a56c4008eee59c789c2ca5ea9df03", + "sha256:588c0e38466306aa7dbe6522ceacf37dde8b13cfa5edde90be2ce382f078875f", + "sha256:6d1bd2e675823a19e6bf72149540ab9851bfe698b796aea698fb926ab2bedd02", + "sha256:aa8e3bd1540dd5c39ef580ec2146a9c99c45f7c62af890095fec9e87b5ca19fb", + "sha256:b978ba1ea90d0abe2fc720ec9a41824b7d3a1304569bd58c9038d8d61dc4dfdb", + "sha256:c85c5367c2e8247e06cc0aba84e3633e90f48e8a0677bc51b351e138b5ff80b1", + "sha256:ce69577b424058bfa177df27213869f37c1e964c3e1ebd3b3d54f1d10b234c4d", + "sha256:ec6eaf98a57624d96d9916352a5bad2d73959f6358fabf43838f7d1a4d2f8389" ], "index": "pypi", - "version": "==0.750" + "version": "==0.760" }, "mypy-extensions": { "hashes": [ @@ -1131,31 +1105,29 @@ }, "regex": { "hashes": [ - "sha256:3d2d5952639615815a5dde7431265fc45b58106ca687ea16b60699c6946bfe02", - "sha256:3dbd8333fd2ebd50977ac8747385a73aa1f546eb6b16fcd83d274470fe11f243", - "sha256:40b7d1291a56897927e08bb973f8c186c2feb14c7f708bfe7aaee09483e85a20", - "sha256:517a827c4c9bcd4a158f48970258c17a52bdd133300c93affe8b5e9ae7b4675d", - "sha256:580e849ba64d7d82019c52d766b1f42cd8a398622278e8f89126800d7e5d52ea", - "sha256:719978a9145d59fc78509ea1d1bb74243f93583ef2a34dcc5623cf8118ae9726", - "sha256:75cf3796f89f75f83207a5c6a6e14eaf57e0369ef0ffff8e22bf36bbcfa0f1de", - "sha256:77396cf80be8b2a35db863cca4c1a902d88ceeb183adab328b81184e71a5eafe", - "sha256:77a3799152951d6d14ae5720ca162c97c64f85d4755da585418eac216b736cad", - "sha256:91235c98283d2bddf1a588f0fbc2da8afa37959294bbd18b76297bdf316ba4d6", - "sha256:99981ccc110b55d3bc2f5140167b8515c57f782ccdd0cd85d8217e1af1f1c632", - "sha256:a07f449fef6b8159080fd0be0e15b593ea55c2760f6340076fecc2845eac6c42", - "sha256:a35d1353ec805cea8a74bc5e141cc20083f2ade3d68bd92aa8b54115f08b902b", - "sha256:a70eb00ea3c18f68102cb125677812f66cacecc5a6187fa48eaef898b3ba45e5", - "sha256:aaffd68c4c1ed891366d5c390081f4bf6337595e76a157baf453603d8e53fbcb", - "sha256:ad9e3c7260809c0d1ded100269f78ea0217c0704f1eaaf40a382008461848b45", - "sha256:ba442ecf7df4c1657db237fa284719d95a8298bbac0056a73aca4e8a127f2968", - "sha256:bbddddfc768052abe88d02010c1f00c7dcd88430b775239fcc44ad2b1c9285b1", - "sha256:c203c9ee755e9656d0af8fab82754d5a664ebaf707b3f883c7eff6a3dd5151cf", - "sha256:c630f23491d0810767d3523b18b9c1e67c35319e298ed7e1a05dea0402ebd424", - "sha256:ccb78a4bf24b150e5b62cf5edff1e5f0b700064407dd0f03a5db7ff8451ee437", - "sha256:e865bc508e316a3a09d36c8621596e6599a203bc54f1cd41020a127ccdac468a", - "sha256:f8e3ca4669f6b09fdcbb3da71b358c7976f203d3e86b9e1be47790ed8915085e" + "sha256:0472acc4b6319801c1bc681d838c88ba1446f9ae199e01f6e41091c701fb3d42", + "sha256:16709434c4e2332ee8ba26ae339aceb8ab0b24b8398ebd0f52ebc943f45c4fc2", + "sha256:223fb63ec8dcab20b3318e93dcec4aee89e98b062934090bf29ffc374d2000a2", + "sha256:23c3ebf05d1cd3adb26723fd598e75724e0cdb7d6a35185ac0caf061cc6edb49", + "sha256:2404a50fb48badaf214b700f08822b68d93d79200e0aefd9569d0332d21fbfcb", + "sha256:2af3a7a16fed6eff85c25da106effa36f61cbbe801d00ade349b53ce7619eb15", + "sha256:37e018d3746baf159aedfc9773c3cafacbd10d354ba15484f5cfc8ed9da5748b", + "sha256:3c9c2988d02a9238a1975c70e87c6ce94e6f36dd8e372b66f468990cfe077434", + "sha256:47298bc8b89d1c747f0f5974aa528fc0b6b17396f1694136a224d51461279d83", + "sha256:4eeb0fe936797ae00a085f99802642bfc722b3b4ea557e9e7849cb621ea10c91", + "sha256:6881be0218b47ed76db033f252bab3f912dfe7ed1fe7baa9daebf51de08546a0", + "sha256:7ac08cee5055f548eed3889e9aaef15fd00172d037949496f1f0b34acb8a7c3e", + "sha256:7c5e2efcf079c35ff266c3f3a6708834f88f9fd04a3c16b855e036b2b7b1b543", + "sha256:8355eaa64724a0fdb010a1654b77cb3e375dc08b7f592cc4a1c05ac606aa481c", + "sha256:999a885f7f5194464238ad5d74b05982acee54002f3aa775d8e0e8c5fb74c06c", + "sha256:9fd2f4813eaa3e421e82819d38e5b634d900faff7ae5a80cd89ccff407175e69", + "sha256:a2e1e53df7dd27943da2b512895125b33fb20f81862c9fed7b3bab2a1de684d1", + "sha256:ab43bc0836820b7900dfffc025b996784aec26ec87dc1df4f95a40398760223f", + "sha256:ba449b56fa419fb19bf2a2438adbd2433f27087a6fe115917eaf9cfca684d5b6", + "sha256:d3f632cefad2cf247bd845794002585e3772288bfcb0dbac59fdecd32cd38b67", + "sha256:d51311496061863caae2cfe120cf1ef37900019b86c89c2d75f0918e0b4b8bf3" ], - "version": "==2019.12.9" + "version": "==2019.12.19" }, "requests": { "hashes": [ From 3bfb6c9621901672be05c5e0fe9a150a28c6b6b9 Mon Sep 17 00:00:00 2001 From: dandds Date: Wed, 18 Dec 2019 17:31:04 -0500 Subject: [PATCH 3/3] Basic implementation for a policy wrapper. The implementation here is meant to wrap a library of JSON policy documents. Policies should be added to directories corresponding to where they will be defined (portfolio, application, environment). Functionality for parsing portfolio policy definitions is included. When the policies need to be defined on a management group, the AzureCloudProvider can iterate the appropriate tier of the policy manager and add those definitions. --- .secrets.baseline | 4 +- atst/domain/csp/cloud.py | 3 ++ atst/domain/csp/policy.py | 43 +++++++++++++++++-- config/base.ini | 1 + .../portfolios/allowed-resource-types.json | 2 +- policies/portfolios/region-restriction.json | 2 +- tests/domain/cloud/test_policy.py | 8 ++++ tests/mock_azure.py | 1 + 8 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 tests/domain/cloud/test_policy.py diff --git a/.secrets.baseline b/.secrets.baseline index ffa18c6e..857d311a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$|^.*pgsslrootcert.yml$", "lines": null }, - "generated_at": "2019-12-18T15:29:41Z", + "generated_at": "2019-12-18T22:26:52Z", "plugins_used": [ { "base64_limit": 4.5, @@ -98,7 +98,7 @@ "hashed_secret": "afc848c316af1a89d49826c5ae9d00ed769415f3", "is_secret": false, "is_verified": false, - "line_number": 29, + "line_number": 30, "type": "Secret Keyword" } ], diff --git a/atst/domain/csp/cloud.py b/atst/domain/csp/cloud.py index adb2260f..36c91c95 100644 --- a/atst/domain/csp/cloud.py +++ b/atst/domain/csp/cloud.py @@ -6,6 +6,7 @@ from atst.models.user import User from atst.models.application import Application from atst.models.environment import Environment from atst.models.environment_role import EnvironmentRole +from .policy import AzurePolicyManager class GeneralCSPException(Exception): @@ -429,6 +430,8 @@ class AzureCloudProvider(CloudProviderInterface): else: self.sdk = azure_sdk_provider + self.policy_manager = AzurePolicyManager(config["AZURE_POLICY_LOCATION"]) + def create_environment( self, auth_credentials: Dict, user: User, environment: Environment ): diff --git a/atst/domain/csp/policy.py b/atst/domain/csp/policy.py index 43b4e232..cfff9aae 100644 --- a/atst/domain/csp/policy.py +++ b/atst/domain/csp/policy.py @@ -1,12 +1,47 @@ +from glob import glob +import json +from dataclasses import dataclass +from os.path import join as path_join + + class AzurePolicyManager: def __init__(self, static_policy_location): self._static_policy_location = static_policy_location - def portfolio_definitions(): + @property + def portfolio_definitions(self): + if getattr(self, "_portfolio_definitions", None) is None: + portfolio_files = self._glob_json("portfolios") + self._portfolio_definitions = self._load_policies(portfolio_files) + + return self._portfolio_definitions + + @property + def application_definitions(self): pass - def application_definitions(): + @property + def environment_definitions(self): pass - def environment_definitions(): - pass + def _glob_json(self, path): + return glob(path_join(self._static_policy_location, "portfolios", "*.json")) + + def _load_policies(self, json_policies): + return [self._load_policy(pol) for pol in json_policies] + + def _load_policy(self, policy_file): + with open(policy_file, "r") as file_: + doc = json.loads(file_.read()) + return AzurePolicy( + definition_point=doc["definitionPoint"], + definition=doc["policyDefinition"], + parameters=doc["parameters"], + ) + + +@dataclass +class AzurePolicy: + definition_point: str + definition: dict + parameters: dict diff --git a/config/base.ini b/config/base.ini index 2cc8fd93..0feba51f 100644 --- a/config/base.ini +++ b/config/base.ini @@ -3,6 +3,7 @@ ASSETS_URL AZURE_ACCOUNT_NAME AZURE_STORAGE_KEY AZURE_TO_BUCKET_NAME +AZURE_POLICY_LOCATION=policies BLOB_STORAGE_URL=http://localhost:8000/ CAC_URL = http://localhost:8000/login-redirect CA_CHAIN = ssl/server-certs/ca-chain.pem diff --git a/policies/portfolios/allowed-resource-types.json b/policies/portfolios/allowed-resource-types.json index 7e4dcb7f..1358a9f4 100644 --- a/policies/portfolios/allowed-resource-types.json +++ b/policies/portfolios/allowed-resource-types.json @@ -1,5 +1,5 @@ { - "definitionPoint": "portfolio-parent", + "definitionPoint": "portfolio", "policyDefinition": { "properties": { "displayName": "Allowed resource types", diff --git a/policies/portfolios/region-restriction.json b/policies/portfolios/region-restriction.json index 9a7f57ba..a828c40e 100644 --- a/policies/portfolios/region-restriction.json +++ b/policies/portfolios/region-restriction.json @@ -1,5 +1,5 @@ { - "definitionPoint": "portfolio-parent", + "definitionPoint": "portfolio", "policyDefinition": { "properties": { "displayName": "Custom - Region Restriction", diff --git a/tests/domain/cloud/test_policy.py b/tests/domain/cloud/test_policy.py new file mode 100644 index 00000000..c0189262 --- /dev/null +++ b/tests/domain/cloud/test_policy.py @@ -0,0 +1,8 @@ +from atst.domain.csp.policy import AzurePolicyManager, AzurePolicy + + +def test_portfolio_definitions(): + manager = AzurePolicyManager("policies") + assert len(manager.portfolio_definitions) > 0 + policy = manager.portfolio_definitions[0] + assert isinstance(policy, AzurePolicy) diff --git a/tests/mock_azure.py b/tests/mock_azure.py index b8391811..417e69fb 100644 --- a/tests/mock_azure.py +++ b/tests/mock_azure.py @@ -7,6 +7,7 @@ AZURE_CONFIG = { "AZURE_CLIENT_ID": "MOCK", "AZURE_SECRET_KEY": "MOCK", "AZURE_TENANT_ID": "MOCK", + "AZURE_POLICY_LOCATION": "policies", } AUTH_CREDENTIALS = {