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